From f39ef4df133889e4c68ebaaceffe2559f3bd4082 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 26 Jun 2023 16:54:08 +0200 Subject: [PATCH 01/10] Build YARP using configure and its Makefile --- mx.truffleruby/mx_truffleruby.py | 18 ++++++++++++++++++ mx.truffleruby/suite.py | 16 +++++++++++++--- src/main/c/yarp/LICENSE.md | 7 +++++++ .../src/yarp_bindings.c | 0 tool/import-yarp.sh | 17 ++++++++++------- 5 files changed, 48 insertions(+), 10 deletions(-) create mode 100644 src/main/c/yarp/LICENSE.md rename src/main/c/{yarp => yarp_bindings}/src/yarp_bindings.c (100%) diff --git a/mx.truffleruby/mx_truffleruby.py b/mx.truffleruby/mx_truffleruby.py index 3674cf984672..84399a86639d 100644 --- a/mx.truffleruby/mx_truffleruby.py +++ b/mx.truffleruby/mx_truffleruby.py @@ -121,6 +121,24 @@ def contents(self, result): command = [jdk.java] + jvm_args + [main_class] + ruby_options + ['"$@"'] return "#!/usr/bin/env bash\n" + "exec " + " ".join(command) + "\n" + +class YARPNativeProject(mx.NativeProject): + def __init__(self, suite, name, deps, workingSets, output=None, **kwArgs): + path = join(root, kwArgs.pop('dir')) + super(YARPNativeProject, self).__init__( + suite, name, subDir=None, srcDirs=[path], deps=deps, workingSets=workingSets, + results=kwArgs.pop('results'), + output=path, d=path, vpath=False, **kwArgs) + + def getBuildTask(self, args): + return YARPNativeBuildTask(args, self) + +class YARPNativeBuildTask(mx.NativeBuildTask): + def build(self): + mx.run(['autoreconf'], cwd=self.subject.dir) + mx.run(['./configure'], cwd=self.subject.dir) + super(YARPNativeBuildTask, self).build() # make + # Commands def jt(*args): diff --git a/mx.truffleruby/suite.py b/mx.truffleruby/suite.py index a5d913d42374..01db2099085d 100644 --- a/mx.truffleruby/suite.py +++ b/mx.truffleruby/suite.py @@ -204,16 +204,26 @@ }, "org.yarp.libyarp": { + "class": "YARPNativeProject", "dir": "src/main/c/yarp", + "makeTarget": "all-no-debug", # Comment this out to build with asserts and no optimizations + "results": ["build/librubyparser.a"], + "description": "YARP used as a static library" + }, + + "org.truffleruby.yarp.bindings": { + "dir": "src/main/c/yarp_bindings", "native": "shared_lib", "deliverable": "yarp", "buildDependencies": [ + "org.yarp.libyarp", # librubyparser.a "org.yarp", # for the generated JNI header file ], "use_jdk_headers": True, # the generated JNI header includes jni.h - "cflags": ["-g", "-Wall", "-Werror", "-pthread"], + "cflags": ["-g", "-Wall", "-Werror", "-pthread", "-I/include"], "ldflags": ["-pthread"], - "description": "YARP + JNI bindings as a single lib" + "ldlibs": ["/build/librubyparser.a"], + "description": "JNI bindings for YARP" }, "org.truffleruby": { @@ -485,7 +495,7 @@ "file:lib/mri", "file:lib/patches", "file:lib/truffle", - "dependency:org.yarp.libyarp", + "dependency:org.truffleruby.yarp.bindings", ], "lib/cext/": [ "file:lib/cext/*.rb", diff --git a/src/main/c/yarp/LICENSE.md b/src/main/c/yarp/LICENSE.md new file mode 100644 index 000000000000..2d449f909f21 --- /dev/null +++ b/src/main/c/yarp/LICENSE.md @@ -0,0 +1,7 @@ +Copyright 2022-present, Shopify Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/src/main/c/yarp/src/yarp_bindings.c b/src/main/c/yarp_bindings/src/yarp_bindings.c similarity index 100% rename from src/main/c/yarp/src/yarp_bindings.c rename to src/main/c/yarp_bindings/src/yarp_bindings.c diff --git a/tool/import-yarp.sh b/tool/import-yarp.sh index e2bf6544cee0..f061e7ca4ec6 100755 --- a/tool/import-yarp.sh +++ b/tool/import-yarp.sh @@ -3,16 +3,19 @@ set -x set -e +YARP=../../yarp + # Create generated files -pushd ../../yarp +pushd $YARP +bundle +bundle exec rake clobber bundle exec rake templates popd -rm -rf src/main/c/yarp/src/yarp -# Copy under src/yarp and not just src/ because we also have yarp_bindings.c -mkdir src/main/c/yarp/src/yarp -cp -R ../../yarp/include src/main/c/yarp/src/yarp -cp -R ../../yarp/src src/main/c/yarp/src/yarp +rm -rf src/main/c/yarp +mkdir src/main/c/yarp +cp -R $YARP/{include,src} src/main/c/yarp +cp $YARP/{configure.ac,LICENSE.md,Makefile.in} src/main/c/yarp rm -rf src/yarp/java -cp -R ../../yarp/java src/yarp/java +cp -R $YARP/java src/yarp/java From b7832f4407818172b6bce717e4dc52dc9cf41ae8 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 29 Jun 2023 15:33:44 +0200 Subject: [PATCH 02/10] Add basic test for YARP --- spec/truffle/yarp_spec.rb | 29 +++++++++++++++++++ .../truffleruby/debug/TruffleDebugNodes.java | 10 +++++-- 2 files changed, 36 insertions(+), 3 deletions(-) create mode 100644 spec/truffle/yarp_spec.rb diff --git a/spec/truffle/yarp_spec.rb b/spec/truffle/yarp_spec.rb new file mode 100644 index 000000000000..686e9159d1f3 --- /dev/null +++ b/spec/truffle/yarp_spec.rb @@ -0,0 +1,29 @@ +# truffleruby_primitives: true + +# Copyright (c) 2023 Oracle and/or its affiliates. All rights reserved. This +# code is released under a tri EPL/GPL/LGPL license. You can use it, +# redistribute it and/or modify it under the terms of the: +# +# Eclipse Public License version 2.0, or +# GNU General Public License version 2, or +# GNU Lesser General Public License version 2.1. + +require_relative '../ruby/spec_helper' + +# Similar tests as in https://github.com/ruby/yarp/blob/main/.github/workflows/truffleruby.yml +describe "YARP" do + it "can parse core files" do + root = File.expand_path("../..", __dir__) + Dir.glob("#{root}/src/main/ruby/truffleruby/**/*.rb") do |file| + Truffle::Debug.yarp_parse(File.read(file)).should.include?("Node") + end + end + + it "can execute simple code" do + -> { + -> { + Truffle::Debug.yarp_execute("p 1+2").should == 3 + }.should output_to_fd(/^YARP AST:.+Truffle AST:/m, STDERR) + }.should output("3\n") + end +end diff --git a/src/main/java/org/truffleruby/debug/TruffleDebugNodes.java b/src/main/java/org/truffleruby/debug/TruffleDebugNodes.java index 8edd96a09469..1adb546d00bc 100644 --- a/src/main/java/org/truffleruby/debug/TruffleDebugNodes.java +++ b/src/main/java/org/truffleruby/debug/TruffleDebugNodes.java @@ -298,15 +298,19 @@ protected Object parse(Object code, @CoreMethod(names = "yarp_execute", onSingleton = true, required = 1) public abstract static class YARPExecuteNode extends CoreMethodArrayArgumentsNode { - @TruffleBoundary @Specialization(guards = "strings.isRubyString(code)", limit = "1") - protected Object yarpExecute(Object code, + protected Object yarpExecute(VirtualFrame frame, Object code, @Cached RubyStringLibrary strings, @Cached TruffleString.CopyToByteArrayNode copyToByteArrayNode) { var tstring = strings.getTString(code); var tencoding = strings.getTEncoding(code); var source = copyToByteArrayNode.execute(tstring, tencoding); + return doExecute(source, RubyArguments.getMethod(frame)); + } + + @TruffleBoundary + private Object doExecute(byte[] source, InternalMethod method) { byte[] serialized = yarpSerialize(getLanguage(), source); var ast = Loader.load(source, serialized); @@ -320,7 +324,7 @@ protected Object yarpExecute(Object code, return truffleAST.getCallTarget().call(RubyArguments.pack( null, null, - null, + method, DeclarationContext.topLevel(getContext()), null, coreLibrary().mainObject, From e39bb1b7a1c00b2da2680ee4cc56757066a35b78 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Thu, 29 Jun 2023 13:47:25 +0200 Subject: [PATCH 03/10] Import ruby/yarp@52a5f4d39a451953f0209facf9fb25e5cabe5386 --- src/main/c/yarp/.gitignore | 48 + src/main/c/yarp/Makefile.in | 52 + src/main/c/yarp/configure.ac | 22 + src/main/c/yarp/include/yarp.h | 69 + src/main/c/yarp/include/yarp/ast.h | 1376 ++ src/main/c/yarp/include/yarp/defines.h | 51 + .../{src/yarp => }/include/yarp/diagnostic.h | 18 +- .../c/yarp/include/yarp/enc/yp_encoding.h | 94 + src/main/c/yarp/include/yarp/node.h | 33 + .../c/yarp/{src/yarp => }/include/yarp/pack.h | 142 +- src/main/c/yarp/include/yarp/parser.h | 398 + .../yarp/{src/yarp => }/include/yarp/regexp.h | 11 +- src/main/c/yarp/include/yarp/unescape.h | 38 + .../yarp => }/include/yarp/util/yp_buffer.h | 29 +- .../yarp => }/include/yarp/util/yp_char.h | 59 +- .../yarp/include/yarp/util/yp_constant_pool.h | 64 + .../yarp => }/include/yarp/util/yp_list.h | 28 +- src/main/c/yarp/include/yarp/util/yp_memchr.h | 14 + .../yarp/include/yarp/util/yp_newline_list.h | 54 + .../include/yarp/util/yp_state_stack.h | 12 +- src/main/c/yarp/include/yarp/util/yp_string.h | 57 + .../c/yarp/include/yarp/util/yp_string_list.h | 28 + .../yarp => }/include/yarp/util/yp_strpbrk.h | 9 +- src/main/c/yarp/src/diagnostic.c | 25 + src/main/c/yarp/src/enc/ascii.c | 65 + src/main/c/yarp/src/enc/big5.c | 79 + src/main/c/yarp/src/enc/euc_jp.c | 82 + src/main/c/yarp/src/enc/gbk.c | 85 + src/main/c/yarp/src/enc/iso_8859_1.c | 50 + src/main/c/yarp/src/enc/iso_8859_10.c | 50 + src/main/c/yarp/src/enc/iso_8859_11.c | 50 + src/main/c/yarp/src/enc/iso_8859_13.c | 50 + src/main/c/yarp/src/enc/iso_8859_14.c | 50 + src/main/c/yarp/src/enc/iso_8859_15.c | 50 + src/main/c/yarp/src/enc/iso_8859_16.c | 50 + src/main/c/yarp/src/enc/iso_8859_2.c | 50 + src/main/c/yarp/src/enc/iso_8859_3.c | 50 + src/main/c/yarp/src/enc/iso_8859_4.c | 50 + src/main/c/yarp/src/enc/iso_8859_5.c | 50 + src/main/c/yarp/src/enc/iso_8859_6.c | 50 + src/main/c/yarp/src/enc/iso_8859_7.c | 50 + src/main/c/yarp/src/enc/iso_8859_8.c | 50 + src/main/c/yarp/src/enc/iso_8859_9.c | 50 + src/main/c/yarp/src/enc/koi8_r.c | 56 + .../c/yarp/src/{yarp/src => }/enc/shared.c | 4 +- src/main/c/yarp/src/enc/shift_jis.c | 82 + src/main/c/yarp/src/enc/unicode.c | 2317 +++ src/main/c/yarp/src/enc/windows_1251.c | 50 + src/main/c/yarp/src/enc/windows_1252.c | 50 + src/main/c/yarp/src/enc/windows_31j.c | 82 + src/main/c/yarp/src/node.c | 1734 +++ src/main/c/yarp/src/pack.c | 493 + src/main/c/yarp/src/prettyprint.c | 1698 ++ src/main/c/yarp/src/regexp.c | 547 + src/main/c/yarp/src/serialize.c | 1532 ++ src/main/c/yarp/src/token_type.c | 337 + src/main/c/yarp/src/unescape.c | 559 + src/main/c/yarp/src/util/yp_buffer.c | 78 + src/main/c/yarp/src/util/yp_char.c | 224 + src/main/c/yarp/src/util/yp_constant_pool.c | 147 + src/main/c/yarp/src/util/yp_list.c | 37 + src/main/c/yarp/src/util/yp_memchr.c | 31 + src/main/c/yarp/src/util/yp_newline_list.c | 117 + src/main/c/yarp/src/util/yp_snprintf.c | 12 + .../src/{yarp/src => }/util/yp_state_stack.c | 8 +- src/main/c/yarp/src/util/yp_string.c | 88 + src/main/c/yarp/src/util/yp_string_list.c | 32 + src/main/c/yarp/src/util/yp_strncasecmp.c | 19 + src/main/c/yarp/src/util/yp_strpbrk.c | 66 + src/main/c/yarp/src/yarp.c | 12896 ++++++++++++++++ src/main/c/yarp/src/yarp/include/yarp.h | 99 - src/main/c/yarp/src/yarp/include/yarp/ast.h | 1155 -- .../c/yarp/src/yarp/include/yarp/defines.h | 12 - .../src/yarp/include/yarp/enc/yp_encoding.h | 388 - .../c/yarp/src/yarp/include/yarp/missing.h | 24 - src/main/c/yarp/src/yarp/include/yarp/node.h | 37 - .../c/yarp/src/yarp/include/yarp/parser.h | 384 - .../c/yarp/src/yarp/include/yarp/unescape.h | 39 - .../yarp/include/yarp/util/yp_conversion.h | 18 - .../src/yarp/include/yarp/util/yp_string.h | 69 - .../yarp/include/yarp/util/yp_string_list.h | 33 - src/main/c/yarp/src/yarp/src/diagnostic.c | 22 - src/main/c/yarp/src/yarp/src/enc/ascii.c | 47 - src/main/c/yarp/src/yarp/src/enc/big5.c | 70 - src/main/c/yarp/src/yarp/src/enc/euc_jp.c | 73 - src/main/c/yarp/src/yarp/src/enc/iso_8859_1.c | 41 - .../c/yarp/src/yarp/src/enc/iso_8859_10.c | 41 - .../c/yarp/src/yarp/src/enc/iso_8859_11.c | 41 - .../c/yarp/src/yarp/src/enc/iso_8859_13.c | 41 - .../c/yarp/src/yarp/src/enc/iso_8859_14.c | 41 - .../c/yarp/src/yarp/src/enc/iso_8859_15.c | 41 - .../c/yarp/src/yarp/src/enc/iso_8859_16.c | 41 - src/main/c/yarp/src/yarp/src/enc/iso_8859_2.c | 41 - src/main/c/yarp/src/yarp/src/enc/iso_8859_3.c | 41 - src/main/c/yarp/src/yarp/src/enc/iso_8859_4.c | 41 - src/main/c/yarp/src/yarp/src/enc/iso_8859_5.c | 41 - src/main/c/yarp/src/yarp/src/enc/iso_8859_6.c | 41 - src/main/c/yarp/src/yarp/src/enc/iso_8859_7.c | 41 - src/main/c/yarp/src/yarp/src/enc/iso_8859_8.c | 41 - src/main/c/yarp/src/yarp/src/enc/iso_8859_9.c | 41 - src/main/c/yarp/src/yarp/src/enc/shift_jis.c | 73 - src/main/c/yarp/src/yarp/src/enc/unicode.c | 2275 --- .../c/yarp/src/yarp/src/enc/windows_1251.c | 41 - .../c/yarp/src/yarp/src/enc/windows_1252.c | 41 - .../c/yarp/src/yarp/src/enc/windows_31j.c | 73 - src/main/c/yarp/src/yarp/src/missing.c | 34 - src/main/c/yarp/src/yarp/src/node.c | 1394 -- src/main/c/yarp/src/yarp/src/pack.c | 493 - src/main/c/yarp/src/yarp/src/prettyprint.c | 1463 -- src/main/c/yarp/src/yarp/src/regexp.c | 518 - src/main/c/yarp/src/yarp/src/serialize.c | 1303 -- src/main/c/yarp/src/yarp/src/token_type.c | 337 - src/main/c/yarp/src/yarp/src/unescape.c | 551 - src/main/c/yarp/src/yarp/src/util/yp_buffer.c | 69 - src/main/c/yarp/src/yarp/src/util/yp_char.c | 203 - .../c/yarp/src/yarp/src/util/yp_conversion.c | 13 - src/main/c/yarp/src/yarp/src/util/yp_list.c | 43 - src/main/c/yarp/src/yarp/src/util/yp_string.c | 94 - .../c/yarp/src/yarp/src/util/yp_string_list.c | 32 - .../c/yarp/src/yarp/src/util/yp_strpbrk.c | 30 - src/main/c/yarp/src/yarp/src/yarp.c | 12066 --------------- .../java/org/yarp/AbstractNodeVisitor.java | 112 +- src/yarp/java/org/yarp/Loader.java | 348 +- src/yarp/java/org/yarp/Nodes.java | 1977 ++- tool/import-yarp.sh | 2 +- 125 files changed, 28486 insertions(+), 25272 deletions(-) create mode 100644 src/main/c/yarp/.gitignore create mode 100644 src/main/c/yarp/Makefile.in create mode 100644 src/main/c/yarp/configure.ac create mode 100644 src/main/c/yarp/include/yarp.h create mode 100644 src/main/c/yarp/include/yarp/ast.h create mode 100644 src/main/c/yarp/include/yarp/defines.h rename src/main/c/yarp/{src/yarp => }/include/yarp/diagnostic.h (57%) create mode 100644 src/main/c/yarp/include/yarp/enc/yp_encoding.h create mode 100644 src/main/c/yarp/include/yarp/node.h rename src/main/c/yarp/{src/yarp => }/include/yarp/pack.h (53%) create mode 100644 src/main/c/yarp/include/yarp/parser.h rename src/main/c/yarp/{src/yarp => }/include/yarp/regexp.h (62%) create mode 100644 src/main/c/yarp/include/yarp/unescape.h rename src/main/c/yarp/{src/yarp => }/include/yarp/util/yp_buffer.h (51%) rename src/main/c/yarp/{src/yarp => }/include/yarp/util/yp_char.h (61%) create mode 100644 src/main/c/yarp/include/yarp/util/yp_constant_pool.h rename src/main/c/yarp/{src/yarp => }/include/yarp/util/yp_list.h (73%) create mode 100644 src/main/c/yarp/include/yarp/util/yp_memchr.h create mode 100644 src/main/c/yarp/include/yarp/util/yp_newline_list.h rename src/main/c/yarp/{src/yarp => }/include/yarp/util/yp_state_stack.h (63%) create mode 100644 src/main/c/yarp/include/yarp/util/yp_string.h create mode 100644 src/main/c/yarp/include/yarp/util/yp_string_list.h rename src/main/c/yarp/{src/yarp => }/include/yarp/util/yp_strpbrk.h (65%) create mode 100644 src/main/c/yarp/src/diagnostic.c create mode 100644 src/main/c/yarp/src/enc/ascii.c create mode 100644 src/main/c/yarp/src/enc/big5.c create mode 100644 src/main/c/yarp/src/enc/euc_jp.c create mode 100644 src/main/c/yarp/src/enc/gbk.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_1.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_10.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_11.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_13.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_14.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_15.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_16.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_2.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_3.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_4.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_5.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_6.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_7.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_8.c create mode 100644 src/main/c/yarp/src/enc/iso_8859_9.c create mode 100644 src/main/c/yarp/src/enc/koi8_r.c rename src/main/c/yarp/src/{yarp/src => }/enc/shared.c (72%) create mode 100644 src/main/c/yarp/src/enc/shift_jis.c create mode 100644 src/main/c/yarp/src/enc/unicode.c create mode 100644 src/main/c/yarp/src/enc/windows_1251.c create mode 100644 src/main/c/yarp/src/enc/windows_1252.c create mode 100644 src/main/c/yarp/src/enc/windows_31j.c create mode 100644 src/main/c/yarp/src/node.c create mode 100644 src/main/c/yarp/src/pack.c create mode 100644 src/main/c/yarp/src/prettyprint.c create mode 100644 src/main/c/yarp/src/regexp.c create mode 100644 src/main/c/yarp/src/serialize.c create mode 100644 src/main/c/yarp/src/token_type.c create mode 100644 src/main/c/yarp/src/unescape.c create mode 100644 src/main/c/yarp/src/util/yp_buffer.c create mode 100644 src/main/c/yarp/src/util/yp_char.c create mode 100644 src/main/c/yarp/src/util/yp_constant_pool.c create mode 100644 src/main/c/yarp/src/util/yp_list.c create mode 100644 src/main/c/yarp/src/util/yp_memchr.c create mode 100644 src/main/c/yarp/src/util/yp_newline_list.c create mode 100644 src/main/c/yarp/src/util/yp_snprintf.c rename src/main/c/yarp/src/{yarp/src => }/util/yp_state_stack.c (81%) create mode 100644 src/main/c/yarp/src/util/yp_string.c create mode 100644 src/main/c/yarp/src/util/yp_string_list.c create mode 100644 src/main/c/yarp/src/util/yp_strncasecmp.c create mode 100644 src/main/c/yarp/src/util/yp_strpbrk.c create mode 100644 src/main/c/yarp/src/yarp.c delete mode 100644 src/main/c/yarp/src/yarp/include/yarp.h delete mode 100644 src/main/c/yarp/src/yarp/include/yarp/ast.h delete mode 100644 src/main/c/yarp/src/yarp/include/yarp/defines.h delete mode 100644 src/main/c/yarp/src/yarp/include/yarp/enc/yp_encoding.h delete mode 100644 src/main/c/yarp/src/yarp/include/yarp/missing.h delete mode 100644 src/main/c/yarp/src/yarp/include/yarp/node.h delete mode 100644 src/main/c/yarp/src/yarp/include/yarp/parser.h delete mode 100644 src/main/c/yarp/src/yarp/include/yarp/unescape.h delete mode 100644 src/main/c/yarp/src/yarp/include/yarp/util/yp_conversion.h delete mode 100644 src/main/c/yarp/src/yarp/include/yarp/util/yp_string.h delete mode 100644 src/main/c/yarp/src/yarp/include/yarp/util/yp_string_list.h delete mode 100644 src/main/c/yarp/src/yarp/src/diagnostic.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/ascii.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/big5.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/euc_jp.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_1.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_10.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_11.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_13.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_14.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_15.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_16.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_2.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_3.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_4.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_5.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_6.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_7.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_8.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/iso_8859_9.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/shift_jis.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/unicode.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/windows_1251.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/windows_1252.c delete mode 100644 src/main/c/yarp/src/yarp/src/enc/windows_31j.c delete mode 100644 src/main/c/yarp/src/yarp/src/missing.c delete mode 100644 src/main/c/yarp/src/yarp/src/node.c delete mode 100644 src/main/c/yarp/src/yarp/src/pack.c delete mode 100644 src/main/c/yarp/src/yarp/src/prettyprint.c delete mode 100644 src/main/c/yarp/src/yarp/src/regexp.c delete mode 100644 src/main/c/yarp/src/yarp/src/serialize.c delete mode 100644 src/main/c/yarp/src/yarp/src/token_type.c delete mode 100644 src/main/c/yarp/src/yarp/src/unescape.c delete mode 100644 src/main/c/yarp/src/yarp/src/util/yp_buffer.c delete mode 100644 src/main/c/yarp/src/yarp/src/util/yp_char.c delete mode 100644 src/main/c/yarp/src/yarp/src/util/yp_conversion.c delete mode 100644 src/main/c/yarp/src/yarp/src/util/yp_list.c delete mode 100644 src/main/c/yarp/src/yarp/src/util/yp_string.c delete mode 100644 src/main/c/yarp/src/yarp/src/util/yp_string_list.c delete mode 100644 src/main/c/yarp/src/yarp/src/util/yp_strpbrk.c delete mode 100644 src/main/c/yarp/src/yarp/src/yarp.c diff --git a/src/main/c/yarp/.gitignore b/src/main/c/yarp/.gitignore new file mode 100644 index 000000000000..4dd8817511fd --- /dev/null +++ b/src/main/c/yarp/.gitignore @@ -0,0 +1,48 @@ +/.bundle/ +/.idea/ +/.yardoc +/_yardoc/ +/coverage/ +/doc/ +/pkg/ +/spec/reports/ +/top-100-gems/ +/tmp/ +/vendor/bundle + +/build/ +/lib/yarp/yarp.* +/lib/yarp.bundle +/lib/yarp.so +test.rb +*.dSYM +*~ + +test.c +a.out + +/ext/yarp/api_node.c +/include/yarp/ast.h +/java/org/yarp/AbstractNodeVisitor.java +/java/org/yarp/Loader.java +/java/org/yarp/Nodes.java +/lib/yarp/node.rb +/lib/yarp/serialize.rb +/src/node.c +/src/prettyprint.c +/src/serialize.c +/src/token_type.c +/src/**/*.o + +compile_commands.json +.cache/ + +autom4te.cache +configure +config.log +config.status +Makefile +/include/yarp/config.h +/config.h.in + +tags diff --git a/src/main/c/yarp/Makefile.in b/src/main/c/yarp/Makefile.in new file mode 100644 index 000000000000..e44522bf441b --- /dev/null +++ b/src/main/c/yarp/Makefile.in @@ -0,0 +1,52 @@ + +# V=0 quiet, V=1 verbose. other values don't work. +V = 0 +V0 = $(V:0=) +Q1 = $(V:1=) +Q = $(Q1:0=@) +ECHO1 = $(V:1=@ :) +ECHO = $(ECHO1:0=@ echo) + +SOEXT := $(shell ruby -e 'puts RbConfig::CONFIG["SOEXT"]') + +DEFS := @DEFS@ +CPPFLAGS := @DEFS@ -Iinclude +CFLAGS := @CFLAGS@ -std=c99 -Wall -Werror -Wextra -Wpedantic -Wundef -Wsign-conversion -fPIC -fvisibility=hidden +CC := @CC@ + +HEADERS := $(shell find include -name '*.h') +SOURCES := $(shell find src -name '*.c') +SHARED_OBJECTS := $(subst src/,build/shared/,$(SOURCES:.c=.o)) +STATIC_OBJECTS := $(subst src/,build/static/,$(SOURCES:.c=.o)) + +all: shared static + +shared: build/librubyparser.$(SOEXT) +static: build/librubyparser.a + +build/librubyparser.$(SOEXT): $(SHARED_OBJECTS) + $(ECHO) "linking $@" + $(Q) $(CC) $(DEBUG_FLAGS) $(CFLAGS) -shared -o $@ $(SHARED_OBJECTS) + +build/librubyparser.a: $(STATIC_OBJECTS) + $(ECHO) "building $@" + $(Q) $(AR) $(ARFLAGS) $@ $(STATIC_OBJECTS) $(Q1:0=>/dev/null) + +build/shared/%.o: src/%.c Makefile $(HEADERS) + $(ECHO) "compiling $@" + $(Q) mkdir -p $(@D) + $(Q) $(CC) $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +build/static/%.o: src/%.c Makefile $(HEADERS) + $(ECHO) "compiling $@" + $(Q) mkdir -p $(@D) + $(Q) $(CC) $(DEBUG_FLAGS) -DYP_STATIC $(CPPFLAGS) $(CFLAGS) -c -o $@ $< + +clean: + $(Q) rm -f -r build + +.PHONY: clean + +all-no-debug: DEBUG_FLAGS := -DNDEBUG=1 +all-no-debug: OPTFLAGS := -O3 +all-no-debug: all diff --git a/src/main/c/yarp/configure.ac b/src/main/c/yarp/configure.ac new file mode 100644 index 000000000000..79e4405fd65e --- /dev/null +++ b/src/main/c/yarp/configure.ac @@ -0,0 +1,22 @@ +m4_define([_YP_VERSION_MAJOR], [0]) +m4_define([_YP_VERSION_MINOR], [4]) +m4_define([_YP_VERSION_PATCH], [0]) +m4_define([_YP_VERSION], [_YP_VERSION_MAJOR._YP_VERSION_MINOR._YP_VERSION_PATCH]) + +AC_INIT([YARP],[_YP_VERSION],[https://github.com/ruby/yarp/issues/new],[yarp],[https://github.com/ruby/yarp]) + +AC_PROG_CC +AC_C_INLINE +AC_DEFINE([_XOPEN_SOURCE], [700], [_XOPEN_SOURCE]) +AC_DEFINE([YP_VERSION_MAJOR], [_YP_VERSION_MAJOR], [YP_VERSION_MAJOR]) +AC_DEFINE([YP_VERSION_MINOR], [_YP_VERSION_MINOR], [YP_VERSION_MINOR]) +AC_DEFINE([YP_VERSION_PATCH], [_YP_VERSION_PATCH], [YP_VERSION_PATCH]) +AC_DEFINE([YP_VERSION], ["_YP_VERSION"], [YP_VERSION]) + +AC_CHECK_FUNCS([mmap]) +AC_CHECK_FUNCS([snprintf]) +AC_CHECK_FUNCS([strncasecmp]) + +AC_CONFIG_HEADERS([include/yarp/config.h:config.h.in]) +AC_CONFIG_FILES([Makefile]) +AC_OUTPUT diff --git a/src/main/c/yarp/include/yarp.h b/src/main/c/yarp/include/yarp.h new file mode 100644 index 000000000000..4bbffdbb1060 --- /dev/null +++ b/src/main/c/yarp/include/yarp.h @@ -0,0 +1,69 @@ +#ifndef YARP_H +#define YARP_H + +#include "yarp/defines.h" +#include "yarp/ast.h" +#include "yarp/diagnostic.h" +#include "yarp/node.h" +#include "yarp/pack.h" +#include "yarp/parser.h" +#include "yarp/regexp.h" +#include "yarp/unescape.h" +#include "yarp/util/yp_buffer.h" +#include "yarp/util/yp_char.h" +#include "yarp/util/yp_memchr.h" +#include "yarp/util/yp_strpbrk.h" + +#include +#include +#include +#include +#include +#include +#include + +#ifndef _WIN32 +#include +#endif + +void yp_serialize_content(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer); + +void yp_print_node(yp_parser_t *parser, yp_node_t *node); + +// The YARP version and the serialization format. +YP_EXPORTED_FUNCTION const char * yp_version(void); + +// Initialize a parser with the given start and end pointers. +YP_EXPORTED_FUNCTION void yp_parser_init(yp_parser_t *parser, const char *source, size_t size, const char *filepath); + +// Register a callback that will be called whenever YARP changes the encoding it +// is using to parse based on the magic comment. +YP_EXPORTED_FUNCTION void yp_parser_register_encoding_changed_callback(yp_parser_t *parser, yp_encoding_changed_callback_t callback); + +// Register a callback that will be called when YARP encounters a magic comment +// with an encoding referenced that it doesn't understand. The callback should +// return NULL if it also doesn't understand the encoding or it should return a +// pointer to a yp_encoding_t struct that contains the functions necessary to +// parse identifiers. +YP_EXPORTED_FUNCTION void yp_parser_register_encoding_decode_callback(yp_parser_t *parser, yp_encoding_decode_callback_t callback); + +// Free any memory associated with the given parser. +YP_EXPORTED_FUNCTION void yp_parser_free(yp_parser_t *parser); + +// Parse the Ruby source associated with the given parser and return the tree. +YP_EXPORTED_FUNCTION yp_node_t * yp_parse(yp_parser_t *parser); + +// Pretty-prints the AST represented by the given node to the given buffer. +YP_EXPORTED_FUNCTION void yp_prettyprint(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer); + +// Serialize the AST represented by the given node to the given buffer. +YP_EXPORTED_FUNCTION void yp_serialize(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer); + +// Parse and serialize the AST represented by the given source to the given +// buffer. +YP_EXPORTED_FUNCTION void yp_parse_serialize(const char *source, size_t size, yp_buffer_t *buffer); + +// Returns a string representation of the given token type. +YP_EXPORTED_FUNCTION const char * yp_token_type_to_str(yp_token_type_t token_type); + +#endif diff --git a/src/main/c/yarp/include/yarp/ast.h b/src/main/c/yarp/include/yarp/ast.h new file mode 100644 index 000000000000..15c8b2855f4d --- /dev/null +++ b/src/main/c/yarp/include/yarp/ast.h @@ -0,0 +1,1376 @@ +/******************************************************************************/ +/* This file is generated by the bin/template script and should not be */ +/* modified manually. See */ +/* templates/include/yarp/ast.h.erb */ +/* if you are looking to modify the */ +/* template */ +/******************************************************************************/ +#ifndef YARP_AST_H +#define YARP_AST_H + +#include "yarp/defines.h" +#include "yarp/util/yp_constant_pool.h" +#include "yarp/util/yp_string.h" + +#include +#include +#include + +// This enum represents every type of token in the Ruby source. +typedef enum yp_token_type { + YP_TOKEN_EOF = 1, // final token in the file + YP_TOKEN_MISSING, // a token that was expected but not found + YP_TOKEN_NOT_PROVIDED, // a token that was not present but it is okay + YP_TOKEN_AMPERSAND, // & + YP_TOKEN_AMPERSAND_AMPERSAND, // && + YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL, // &&= + YP_TOKEN_AMPERSAND_DOT, // &. + YP_TOKEN_AMPERSAND_EQUAL, // &= + YP_TOKEN_BACKTICK, // ` + YP_TOKEN_BACK_REFERENCE, // a back reference + YP_TOKEN_BANG, // ! or !@ + YP_TOKEN_BANG_EQUAL, // != + YP_TOKEN_BANG_TILDE, // !~ + YP_TOKEN_BRACE_LEFT, // { + YP_TOKEN_BRACE_RIGHT, // } + YP_TOKEN_BRACKET_LEFT, // [ + YP_TOKEN_BRACKET_LEFT_ARRAY, // [ for the beginning of an array + YP_TOKEN_BRACKET_LEFT_RIGHT, // [] + YP_TOKEN_BRACKET_LEFT_RIGHT_EQUAL, // []= + YP_TOKEN_BRACKET_RIGHT, // ] + YP_TOKEN_CARET, // ^ + YP_TOKEN_CARET_EQUAL, // ^= + YP_TOKEN_CHARACTER_LITERAL, // a character literal + YP_TOKEN_CLASS_VARIABLE, // a class variable + YP_TOKEN_COLON, // : + YP_TOKEN_COLON_COLON, // :: + YP_TOKEN_COMMA, // , + YP_TOKEN_COMMENT, // a comment + YP_TOKEN_CONSTANT, // a constant + YP_TOKEN_DOT, // . + YP_TOKEN_DOT_DOT, // .. + YP_TOKEN_DOT_DOT_DOT, // ... + YP_TOKEN_EMBDOC_BEGIN, // =begin + YP_TOKEN_EMBDOC_END, // =end + YP_TOKEN_EMBDOC_LINE, // a line inside of embedded documentation + YP_TOKEN_EMBEXPR_BEGIN, // #{ + YP_TOKEN_EMBEXPR_END, // } + YP_TOKEN_EMBVAR, // # + YP_TOKEN_EQUAL, // = + YP_TOKEN_EQUAL_EQUAL, // == + YP_TOKEN_EQUAL_EQUAL_EQUAL, // === + YP_TOKEN_EQUAL_GREATER, // => + YP_TOKEN_EQUAL_TILDE, // =~ + YP_TOKEN_FLOAT, // a floating point number + YP_TOKEN_GLOBAL_VARIABLE, // a global variable + YP_TOKEN_GREATER, // > + YP_TOKEN_GREATER_EQUAL, // >= + YP_TOKEN_GREATER_GREATER, // >> + YP_TOKEN_GREATER_GREATER_EQUAL, // >>= + YP_TOKEN_HEREDOC_END, // the end of a heredoc + YP_TOKEN_HEREDOC_START, // the start of a heredoc + YP_TOKEN_IDENTIFIER, // an identifier + YP_TOKEN_IGNORED_NEWLINE, // an ignored newline + YP_TOKEN_IMAGINARY_NUMBER, // an imaginary number literal + YP_TOKEN_INSTANCE_VARIABLE, // an instance variable + YP_TOKEN_INTEGER, // an integer (any base) + YP_TOKEN_KEYWORD_ALIAS, // alias + YP_TOKEN_KEYWORD_AND, // and + YP_TOKEN_KEYWORD_BEGIN, // begin + YP_TOKEN_KEYWORD_BEGIN_UPCASE, // BEGIN + YP_TOKEN_KEYWORD_BREAK, // break + YP_TOKEN_KEYWORD_CASE, // case + YP_TOKEN_KEYWORD_CLASS, // class + YP_TOKEN_KEYWORD_DEF, // def + YP_TOKEN_KEYWORD_DEFINED, // defined? + YP_TOKEN_KEYWORD_DO, // do + YP_TOKEN_KEYWORD_DO_LOOP, // do keyword for a predicate in a while, until, or for loop + YP_TOKEN_KEYWORD_ELSE, // else + YP_TOKEN_KEYWORD_ELSIF, // elsif + YP_TOKEN_KEYWORD_END, // end + YP_TOKEN_KEYWORD_END_UPCASE, // END + YP_TOKEN_KEYWORD_ENSURE, // ensure + YP_TOKEN_KEYWORD_FALSE, // false + YP_TOKEN_KEYWORD_FOR, // for + YP_TOKEN_KEYWORD_IF, // if + YP_TOKEN_KEYWORD_IF_MODIFIER, // if in the modifier form + YP_TOKEN_KEYWORD_IN, // in + YP_TOKEN_KEYWORD_MODULE, // module + YP_TOKEN_KEYWORD_NEXT, // next + YP_TOKEN_KEYWORD_NIL, // nil + YP_TOKEN_KEYWORD_NOT, // not + YP_TOKEN_KEYWORD_OR, // or + YP_TOKEN_KEYWORD_REDO, // redo + YP_TOKEN_KEYWORD_RESCUE, // rescue + YP_TOKEN_KEYWORD_RESCUE_MODIFIER, // rescue in the modifier form + YP_TOKEN_KEYWORD_RETRY, // retry + YP_TOKEN_KEYWORD_RETURN, // return + YP_TOKEN_KEYWORD_SELF, // self + YP_TOKEN_KEYWORD_SUPER, // super + YP_TOKEN_KEYWORD_THEN, // then + YP_TOKEN_KEYWORD_TRUE, // true + YP_TOKEN_KEYWORD_UNDEF, // undef + YP_TOKEN_KEYWORD_UNLESS, // unless + YP_TOKEN_KEYWORD_UNLESS_MODIFIER, // unless in the modifier form + YP_TOKEN_KEYWORD_UNTIL, // until + YP_TOKEN_KEYWORD_UNTIL_MODIFIER, // until in the modifier form + YP_TOKEN_KEYWORD_WHEN, // when + YP_TOKEN_KEYWORD_WHILE, // while + YP_TOKEN_KEYWORD_WHILE_MODIFIER, // while in the modifier form + YP_TOKEN_KEYWORD_YIELD, // yield + YP_TOKEN_KEYWORD___ENCODING__, // __ENCODING__ + YP_TOKEN_KEYWORD___FILE__, // __FILE__ + YP_TOKEN_KEYWORD___LINE__, // __LINE__ + YP_TOKEN_LABEL, // a label + YP_TOKEN_LABEL_END, // the end of a label + YP_TOKEN_LAMBDA_BEGIN, // { + YP_TOKEN_LESS, // < + YP_TOKEN_LESS_EQUAL, // <= + YP_TOKEN_LESS_EQUAL_GREATER, // <=> + YP_TOKEN_LESS_LESS, // << + YP_TOKEN_LESS_LESS_EQUAL, // <<= + YP_TOKEN_MINUS, // - + YP_TOKEN_MINUS_EQUAL, // -= + YP_TOKEN_MINUS_GREATER, // -> + YP_TOKEN_NEWLINE, // a newline character outside of other tokens + YP_TOKEN_NUMBERED_REFERENCE, // a numbered reference to a capture group in the previous regular expression match + YP_TOKEN_PARENTHESIS_LEFT, // ( + YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES, // ( for a parentheses node + YP_TOKEN_PARENTHESIS_RIGHT, // ) + YP_TOKEN_PERCENT, // % + YP_TOKEN_PERCENT_EQUAL, // %= + YP_TOKEN_PERCENT_LOWER_I, // %i + YP_TOKEN_PERCENT_LOWER_W, // %w + YP_TOKEN_PERCENT_LOWER_X, // %x + YP_TOKEN_PERCENT_UPPER_I, // %I + YP_TOKEN_PERCENT_UPPER_W, // %W + YP_TOKEN_PIPE, // | + YP_TOKEN_PIPE_EQUAL, // |= + YP_TOKEN_PIPE_PIPE, // || + YP_TOKEN_PIPE_PIPE_EQUAL, // ||= + YP_TOKEN_PLUS, // + + YP_TOKEN_PLUS_EQUAL, // += + YP_TOKEN_QUESTION_MARK, // ? + YP_TOKEN_RATIONAL_NUMBER, // a rational number literal + YP_TOKEN_REGEXP_BEGIN, // the beginning of a regular expression + YP_TOKEN_REGEXP_END, // the end of a regular expression + YP_TOKEN_SEMICOLON, // ; + YP_TOKEN_SLASH, // / + YP_TOKEN_SLASH_EQUAL, // /= + YP_TOKEN_STAR, // * + YP_TOKEN_STAR_EQUAL, // *= + YP_TOKEN_STAR_STAR, // ** + YP_TOKEN_STAR_STAR_EQUAL, // **= + YP_TOKEN_STRING_BEGIN, // the beginning of a string + YP_TOKEN_STRING_CONTENT, // the contents of a string + YP_TOKEN_STRING_END, // the end of a string + YP_TOKEN_SYMBOL_BEGIN, // the beginning of a symbol + YP_TOKEN_TILDE, // ~ or ~@ + YP_TOKEN_UCOLON_COLON, // unary :: + YP_TOKEN_UDOT_DOT, // unary .. + YP_TOKEN_UDOT_DOT_DOT, // unary ... + YP_TOKEN_UMINUS, // -@ + YP_TOKEN_UMINUS_NUM, // -@ for a number + YP_TOKEN_UPLUS, // +@ + YP_TOKEN_USTAR, // unary * + YP_TOKEN_USTAR_STAR, // unary ** + YP_TOKEN_WORDS_SEP, // a separator between words in a list + YP_TOKEN___END__, // marker for the point in the file at which the parser should stop + YP_TOKEN_MAXIMUM, // the maximum token value +} yp_token_type_t; + +// This struct represents a token in the Ruby source. We use it to track both +// type and location information. +typedef struct { + yp_token_type_t type; + const char *start; + const char *end; +} yp_token_t; + +// This represents a range of bytes in the source string to which a node or +// token corresponds. +typedef struct { + const char *start; + const char *end; +} yp_location_t; + +typedef struct { + yp_location_t *locations; + size_t size; + size_t capacity; +} yp_location_list_t; + +struct yp_node; + +typedef struct yp_node_list { + struct yp_node **nodes; + size_t size; + size_t capacity; +} yp_node_list_t; + +typedef enum { + YP_NODE_ALIAS_NODE = 1, + YP_NODE_ALTERNATION_PATTERN_NODE = 2, + YP_NODE_AND_NODE = 3, + YP_NODE_ARGUMENTS_NODE = 4, + YP_NODE_ARRAY_NODE = 5, + YP_NODE_ARRAY_PATTERN_NODE = 6, + YP_NODE_ASSOC_NODE = 7, + YP_NODE_ASSOC_SPLAT_NODE = 8, + YP_NODE_BACK_REFERENCE_READ_NODE = 9, + YP_NODE_BEGIN_NODE = 10, + YP_NODE_BLOCK_ARGUMENT_NODE = 11, + YP_NODE_BLOCK_NODE = 12, + YP_NODE_BLOCK_PARAMETER_NODE = 13, + YP_NODE_BLOCK_PARAMETERS_NODE = 14, + YP_NODE_BREAK_NODE = 15, + YP_NODE_CALL_NODE = 16, + YP_NODE_CALL_OPERATOR_AND_WRITE_NODE = 17, + YP_NODE_CALL_OPERATOR_OR_WRITE_NODE = 18, + YP_NODE_CALL_OPERATOR_WRITE_NODE = 19, + YP_NODE_CAPTURE_PATTERN_NODE = 20, + YP_NODE_CASE_NODE = 21, + YP_NODE_CLASS_NODE = 22, + YP_NODE_CLASS_VARIABLE_OPERATOR_AND_WRITE_NODE = 23, + YP_NODE_CLASS_VARIABLE_OPERATOR_OR_WRITE_NODE = 24, + YP_NODE_CLASS_VARIABLE_OPERATOR_WRITE_NODE = 25, + YP_NODE_CLASS_VARIABLE_READ_NODE = 26, + YP_NODE_CLASS_VARIABLE_WRITE_NODE = 27, + YP_NODE_CONSTANT_OPERATOR_AND_WRITE_NODE = 28, + YP_NODE_CONSTANT_OPERATOR_OR_WRITE_NODE = 29, + YP_NODE_CONSTANT_OPERATOR_WRITE_NODE = 30, + YP_NODE_CONSTANT_PATH_NODE = 31, + YP_NODE_CONSTANT_PATH_OPERATOR_AND_WRITE_NODE = 32, + YP_NODE_CONSTANT_PATH_OPERATOR_OR_WRITE_NODE = 33, + YP_NODE_CONSTANT_PATH_OPERATOR_WRITE_NODE = 34, + YP_NODE_CONSTANT_PATH_WRITE_NODE = 35, + YP_NODE_CONSTANT_READ_NODE = 36, + YP_NODE_DEF_NODE = 37, + YP_NODE_DEFINED_NODE = 38, + YP_NODE_ELSE_NODE = 39, + YP_NODE_EMBEDDED_STATEMENTS_NODE = 40, + YP_NODE_EMBEDDED_VARIABLE_NODE = 41, + YP_NODE_ENSURE_NODE = 42, + YP_NODE_FALSE_NODE = 43, + YP_NODE_FIND_PATTERN_NODE = 44, + YP_NODE_FLOAT_NODE = 45, + YP_NODE_FOR_NODE = 46, + YP_NODE_FORWARDING_ARGUMENTS_NODE = 47, + YP_NODE_FORWARDING_PARAMETER_NODE = 48, + YP_NODE_FORWARDING_SUPER_NODE = 49, + YP_NODE_GLOBAL_VARIABLE_OPERATOR_AND_WRITE_NODE = 50, + YP_NODE_GLOBAL_VARIABLE_OPERATOR_OR_WRITE_NODE = 51, + YP_NODE_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE = 52, + YP_NODE_GLOBAL_VARIABLE_READ_NODE = 53, + YP_NODE_GLOBAL_VARIABLE_WRITE_NODE = 54, + YP_NODE_HASH_NODE = 55, + YP_NODE_HASH_PATTERN_NODE = 56, + YP_NODE_IF_NODE = 57, + YP_NODE_IMAGINARY_NODE = 58, + YP_NODE_IN_NODE = 59, + YP_NODE_INSTANCE_VARIABLE_OPERATOR_AND_WRITE_NODE = 60, + YP_NODE_INSTANCE_VARIABLE_OPERATOR_OR_WRITE_NODE = 61, + YP_NODE_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE = 62, + YP_NODE_INSTANCE_VARIABLE_READ_NODE = 63, + YP_NODE_INSTANCE_VARIABLE_WRITE_NODE = 64, + YP_NODE_INTEGER_NODE = 65, + YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE = 66, + YP_NODE_INTERPOLATED_STRING_NODE = 67, + YP_NODE_INTERPOLATED_SYMBOL_NODE = 68, + YP_NODE_INTERPOLATED_X_STRING_NODE = 69, + YP_NODE_KEYWORD_HASH_NODE = 70, + YP_NODE_KEYWORD_PARAMETER_NODE = 71, + YP_NODE_KEYWORD_REST_PARAMETER_NODE = 72, + YP_NODE_LAMBDA_NODE = 73, + YP_NODE_LOCAL_VARIABLE_OPERATOR_AND_WRITE_NODE = 74, + YP_NODE_LOCAL_VARIABLE_OPERATOR_OR_WRITE_NODE = 75, + YP_NODE_LOCAL_VARIABLE_OPERATOR_WRITE_NODE = 76, + YP_NODE_LOCAL_VARIABLE_READ_NODE = 77, + YP_NODE_LOCAL_VARIABLE_WRITE_NODE = 78, + YP_NODE_MATCH_PREDICATE_NODE = 79, + YP_NODE_MATCH_REQUIRED_NODE = 80, + YP_NODE_MISSING_NODE = 81, + YP_NODE_MODULE_NODE = 82, + YP_NODE_MULTI_WRITE_NODE = 83, + YP_NODE_NEXT_NODE = 84, + YP_NODE_NIL_NODE = 85, + YP_NODE_NO_KEYWORDS_PARAMETER_NODE = 86, + YP_NODE_NUMBERED_REFERENCE_READ_NODE = 87, + YP_NODE_OPTIONAL_PARAMETER_NODE = 88, + YP_NODE_OR_NODE = 89, + YP_NODE_PARAMETERS_NODE = 90, + YP_NODE_PARENTHESES_NODE = 91, + YP_NODE_PINNED_EXPRESSION_NODE = 92, + YP_NODE_PINNED_VARIABLE_NODE = 93, + YP_NODE_POST_EXECUTION_NODE = 94, + YP_NODE_PRE_EXECUTION_NODE = 95, + YP_NODE_PROGRAM_NODE = 96, + YP_NODE_RANGE_NODE = 97, + YP_NODE_RATIONAL_NODE = 98, + YP_NODE_REDO_NODE = 99, + YP_NODE_REGULAR_EXPRESSION_NODE = 100, + YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE = 101, + YP_NODE_REQUIRED_PARAMETER_NODE = 102, + YP_NODE_RESCUE_MODIFIER_NODE = 103, + YP_NODE_RESCUE_NODE = 104, + YP_NODE_REST_PARAMETER_NODE = 105, + YP_NODE_RETRY_NODE = 106, + YP_NODE_RETURN_NODE = 107, + YP_NODE_SELF_NODE = 108, + YP_NODE_SINGLETON_CLASS_NODE = 109, + YP_NODE_SOURCE_ENCODING_NODE = 110, + YP_NODE_SOURCE_FILE_NODE = 111, + YP_NODE_SOURCE_LINE_NODE = 112, + YP_NODE_SPLAT_NODE = 113, + YP_NODE_STATEMENTS_NODE = 114, + YP_NODE_STRING_CONCAT_NODE = 115, + YP_NODE_STRING_NODE = 116, + YP_NODE_SUPER_NODE = 117, + YP_NODE_SYMBOL_NODE = 118, + YP_NODE_TRUE_NODE = 119, + YP_NODE_UNDEF_NODE = 120, + YP_NODE_UNLESS_NODE = 121, + YP_NODE_UNTIL_NODE = 122, + YP_NODE_WHEN_NODE = 123, + YP_NODE_WHILE_NODE = 124, + YP_NODE_X_STRING_NODE = 125, + YP_NODE_YIELD_NODE = 126, +} yp_node_type_t; + +// This is the overall tagged union representing a node in the syntax tree. +typedef struct yp_node { + // This represents the type of the node. It somewhat maps to the nodes that + // existed in the original grammar and ripper, but it's not a 1:1 mapping. + yp_node_type_t type; + + // This is the location of the node in the source. It's a range of bytes + // containing a start and an end. + yp_location_t location; +} yp_node_t; + +// AliasNode +typedef struct yp_alias_node { + yp_node_t base; + struct yp_node *new_name; + struct yp_node *old_name; + yp_location_t keyword_loc; +} yp_alias_node_t; + +// AlternationPatternNode +typedef struct yp_alternation_pattern_node { + yp_node_t base; + struct yp_node *left; + struct yp_node *right; + yp_location_t operator_loc; +} yp_alternation_pattern_node_t; + +// AndNode +typedef struct yp_and_node { + yp_node_t base; + struct yp_node *left; + struct yp_node *right; + yp_location_t operator_loc; +} yp_and_node_t; + +// ArgumentsNode +typedef struct yp_arguments_node { + yp_node_t base; + struct yp_node_list arguments; +} yp_arguments_node_t; + +// ArrayNode +typedef struct yp_array_node { + yp_node_t base; + struct yp_node_list elements; + yp_location_t opening_loc; + yp_location_t closing_loc; +} yp_array_node_t; + +// ArrayPatternNode +typedef struct yp_array_pattern_node { + yp_node_t base; + struct yp_node *constant; + struct yp_node_list requireds; + struct yp_node *rest; + struct yp_node_list posts; + yp_location_t opening_loc; + yp_location_t closing_loc; +} yp_array_pattern_node_t; + +// AssocNode +typedef struct yp_assoc_node { + yp_node_t base; + struct yp_node *key; + struct yp_node *value; + yp_location_t operator_loc; +} yp_assoc_node_t; + +// AssocSplatNode +typedef struct yp_assoc_splat_node { + yp_node_t base; + struct yp_node *value; + yp_location_t operator_loc; +} yp_assoc_splat_node_t; + +// BackReferenceReadNode +typedef struct yp_back_reference_read_node { + yp_node_t base; +} yp_back_reference_read_node_t; + +// BeginNode +typedef struct yp_begin_node { + yp_node_t base; + yp_location_t begin_keyword_loc; + struct yp_statements_node *statements; + struct yp_rescue_node *rescue_clause; + struct yp_else_node *else_clause; + struct yp_ensure_node *ensure_clause; + yp_location_t end_keyword_loc; +} yp_begin_node_t; + +// BlockArgumentNode +typedef struct yp_block_argument_node { + yp_node_t base; + struct yp_node *expression; + yp_location_t operator_loc; +} yp_block_argument_node_t; + +// BlockNode +typedef struct yp_block_node { + yp_node_t base; + yp_constant_id_list_t locals; + struct yp_block_parameters_node *parameters; + struct yp_node *statements; + yp_location_t opening_loc; + yp_location_t closing_loc; +} yp_block_node_t; + +// BlockParameterNode +typedef struct yp_block_parameter_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; +} yp_block_parameter_node_t; + +// BlockParametersNode +typedef struct yp_block_parameters_node { + yp_node_t base; + struct yp_parameters_node *parameters; + yp_location_list_t locals; + yp_location_t opening_loc; + yp_location_t closing_loc; +} yp_block_parameters_node_t; + +// BreakNode +typedef struct yp_break_node { + yp_node_t base; + struct yp_arguments_node *arguments; + yp_location_t keyword_loc; +} yp_break_node_t; + +// CallNode +typedef struct yp_call_node { + yp_node_t base; + struct yp_node *receiver; + yp_location_t operator_loc; + yp_location_t message_loc; + yp_location_t opening_loc; + struct yp_arguments_node *arguments; + yp_location_t closing_loc; + struct yp_block_node *block; + uint32_t flags; + yp_string_t name; +} yp_call_node_t; + +// CallOperatorAndWriteNode +typedef struct yp_call_operator_and_write_node { + yp_node_t base; + struct yp_call_node *target; + yp_location_t operator_loc; + struct yp_node *value; +} yp_call_operator_and_write_node_t; + +// CallOperatorOrWriteNode +typedef struct yp_call_operator_or_write_node { + yp_node_t base; + struct yp_call_node *target; + struct yp_node *value; + yp_location_t operator_loc; +} yp_call_operator_or_write_node_t; + +// CallOperatorWriteNode +typedef struct yp_call_operator_write_node { + yp_node_t base; + struct yp_call_node *target; + yp_location_t operator_loc; + struct yp_node *value; + yp_constant_id_t operator_id; +} yp_call_operator_write_node_t; + +// CapturePatternNode +typedef struct yp_capture_pattern_node { + yp_node_t base; + struct yp_node *value; + struct yp_node *target; + yp_location_t operator_loc; +} yp_capture_pattern_node_t; + +// CaseNode +typedef struct yp_case_node { + yp_node_t base; + struct yp_node *predicate; + struct yp_node_list conditions; + struct yp_else_node *consequent; + yp_location_t case_keyword_loc; + yp_location_t end_keyword_loc; +} yp_case_node_t; + +// ClassNode +typedef struct yp_class_node { + yp_node_t base; + yp_constant_id_list_t locals; + yp_location_t class_keyword_loc; + struct yp_node *constant_path; + yp_location_t inheritance_operator_loc; + struct yp_node *superclass; + struct yp_node *statements; + yp_location_t end_keyword_loc; +} yp_class_node_t; + +// ClassVariableOperatorAndWriteNode +typedef struct yp_class_variable_operator_and_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; +} yp_class_variable_operator_and_write_node_t; + +// ClassVariableOperatorOrWriteNode +typedef struct yp_class_variable_operator_or_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; +} yp_class_variable_operator_or_write_node_t; + +// ClassVariableOperatorWriteNode +typedef struct yp_class_variable_operator_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; + yp_constant_id_t operator; +} yp_class_variable_operator_write_node_t; + +// ClassVariableReadNode +typedef struct yp_class_variable_read_node { + yp_node_t base; +} yp_class_variable_read_node_t; + +// ClassVariableWriteNode +typedef struct yp_class_variable_write_node { + yp_node_t base; + yp_location_t name_loc; + struct yp_node *value; + yp_location_t operator_loc; +} yp_class_variable_write_node_t; + +// ConstantOperatorAndWriteNode +typedef struct yp_constant_operator_and_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; +} yp_constant_operator_and_write_node_t; + +// ConstantOperatorOrWriteNode +typedef struct yp_constant_operator_or_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; +} yp_constant_operator_or_write_node_t; + +// ConstantOperatorWriteNode +typedef struct yp_constant_operator_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; + yp_constant_id_t operator; +} yp_constant_operator_write_node_t; + +// ConstantPathNode +typedef struct yp_constant_path_node { + yp_node_t base; + struct yp_node *parent; + struct yp_node *child; + yp_location_t delimiter_loc; +} yp_constant_path_node_t; + +// ConstantPathOperatorAndWriteNode +typedef struct yp_constant_path_operator_and_write_node { + yp_node_t base; + struct yp_constant_path_node *target; + yp_location_t operator_loc; + struct yp_node *value; +} yp_constant_path_operator_and_write_node_t; + +// ConstantPathOperatorOrWriteNode +typedef struct yp_constant_path_operator_or_write_node { + yp_node_t base; + struct yp_constant_path_node *target; + yp_location_t operator_loc; + struct yp_node *value; +} yp_constant_path_operator_or_write_node_t; + +// ConstantPathOperatorWriteNode +typedef struct yp_constant_path_operator_write_node { + yp_node_t base; + struct yp_constant_path_node *target; + yp_location_t operator_loc; + struct yp_node *value; + yp_constant_id_t operator; +} yp_constant_path_operator_write_node_t; + +// ConstantPathWriteNode +typedef struct yp_constant_path_write_node { + yp_node_t base; + struct yp_node *target; + yp_location_t operator_loc; + struct yp_node *value; +} yp_constant_path_write_node_t; + +// ConstantReadNode +typedef struct yp_constant_read_node { + yp_node_t base; +} yp_constant_read_node_t; + +// DefNode +typedef struct yp_def_node { + yp_node_t base; + yp_location_t name_loc; + struct yp_node *receiver; + struct yp_parameters_node *parameters; + struct yp_node *statements; + yp_constant_id_list_t locals; + yp_location_t def_keyword_loc; + yp_location_t operator_loc; + yp_location_t lparen_loc; + yp_location_t rparen_loc; + yp_location_t equal_loc; + yp_location_t end_keyword_loc; +} yp_def_node_t; + +// DefinedNode +typedef struct yp_defined_node { + yp_node_t base; + yp_location_t lparen_loc; + struct yp_node *value; + yp_location_t rparen_loc; + yp_location_t keyword_loc; +} yp_defined_node_t; + +// ElseNode +typedef struct yp_else_node { + yp_node_t base; + yp_location_t else_keyword_loc; + struct yp_statements_node *statements; + yp_location_t end_keyword_loc; +} yp_else_node_t; + +// EmbeddedStatementsNode +typedef struct yp_embedded_statements_node { + yp_node_t base; + yp_location_t opening_loc; + struct yp_statements_node *statements; + yp_location_t closing_loc; +} yp_embedded_statements_node_t; + +// EmbeddedVariableNode +typedef struct yp_embedded_variable_node { + yp_node_t base; + yp_location_t operator_loc; + struct yp_node *variable; +} yp_embedded_variable_node_t; + +// EnsureNode +typedef struct yp_ensure_node { + yp_node_t base; + yp_location_t ensure_keyword_loc; + struct yp_statements_node *statements; + yp_location_t end_keyword_loc; +} yp_ensure_node_t; + +// FalseNode +typedef struct yp_false_node { + yp_node_t base; +} yp_false_node_t; + +// FindPatternNode +typedef struct yp_find_pattern_node { + yp_node_t base; + struct yp_node *constant; + struct yp_node *left; + struct yp_node_list requireds; + struct yp_node *right; + yp_location_t opening_loc; + yp_location_t closing_loc; +} yp_find_pattern_node_t; + +// FloatNode +typedef struct yp_float_node { + yp_node_t base; +} yp_float_node_t; + +// ForNode +typedef struct yp_for_node { + yp_node_t base; + struct yp_node *index; + struct yp_node *collection; + struct yp_statements_node *statements; + yp_location_t for_keyword_loc; + yp_location_t in_keyword_loc; + yp_location_t do_keyword_loc; + yp_location_t end_keyword_loc; +} yp_for_node_t; + +// ForwardingArgumentsNode +typedef struct yp_forwarding_arguments_node { + yp_node_t base; +} yp_forwarding_arguments_node_t; + +// ForwardingParameterNode +typedef struct yp_forwarding_parameter_node { + yp_node_t base; +} yp_forwarding_parameter_node_t; + +// ForwardingSuperNode +typedef struct yp_forwarding_super_node { + yp_node_t base; + struct yp_block_node *block; +} yp_forwarding_super_node_t; + +// GlobalVariableOperatorAndWriteNode +typedef struct yp_global_variable_operator_and_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; +} yp_global_variable_operator_and_write_node_t; + +// GlobalVariableOperatorOrWriteNode +typedef struct yp_global_variable_operator_or_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; +} yp_global_variable_operator_or_write_node_t; + +// GlobalVariableOperatorWriteNode +typedef struct yp_global_variable_operator_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; + yp_constant_id_t operator; +} yp_global_variable_operator_write_node_t; + +// GlobalVariableReadNode +typedef struct yp_global_variable_read_node { + yp_node_t base; +} yp_global_variable_read_node_t; + +// GlobalVariableWriteNode +typedef struct yp_global_variable_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; +} yp_global_variable_write_node_t; + +// HashNode +typedef struct yp_hash_node { + yp_node_t base; + yp_location_t opening_loc; + struct yp_node_list elements; + yp_location_t closing_loc; +} yp_hash_node_t; + +// HashPatternNode +typedef struct yp_hash_pattern_node { + yp_node_t base; + struct yp_node *constant; + struct yp_node_list assocs; + struct yp_node *kwrest; + yp_location_t opening_loc; + yp_location_t closing_loc; +} yp_hash_pattern_node_t; + +// IfNode +typedef struct yp_if_node { + yp_node_t base; + yp_location_t if_keyword_loc; + struct yp_node *predicate; + struct yp_statements_node *statements; + struct yp_node *consequent; + yp_location_t end_keyword_loc; +} yp_if_node_t; + +// ImaginaryNode +typedef struct yp_imaginary_node { + yp_node_t base; + struct yp_node *numeric; +} yp_imaginary_node_t; + +// InNode +typedef struct yp_in_node { + yp_node_t base; + struct yp_node *pattern; + struct yp_statements_node *statements; + yp_location_t in_loc; + yp_location_t then_loc; +} yp_in_node_t; + +// InstanceVariableOperatorAndWriteNode +typedef struct yp_instance_variable_operator_and_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; +} yp_instance_variable_operator_and_write_node_t; + +// InstanceVariableOperatorOrWriteNode +typedef struct yp_instance_variable_operator_or_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; +} yp_instance_variable_operator_or_write_node_t; + +// InstanceVariableOperatorWriteNode +typedef struct yp_instance_variable_operator_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; + yp_constant_id_t operator; +} yp_instance_variable_operator_write_node_t; + +// InstanceVariableReadNode +typedef struct yp_instance_variable_read_node { + yp_node_t base; +} yp_instance_variable_read_node_t; + +// InstanceVariableWriteNode +typedef struct yp_instance_variable_write_node { + yp_node_t base; + yp_location_t name_loc; + struct yp_node *value; + yp_location_t operator_loc; +} yp_instance_variable_write_node_t; + +// IntegerNode +typedef struct yp_integer_node { + yp_node_t base; +} yp_integer_node_t; + +// InterpolatedRegularExpressionNode +typedef struct yp_interpolated_regular_expression_node { + yp_node_t base; + yp_location_t opening_loc; + struct yp_node_list parts; + yp_location_t closing_loc; + uint32_t flags; +} yp_interpolated_regular_expression_node_t; + +// InterpolatedStringNode +typedef struct yp_interpolated_string_node { + yp_node_t base; + yp_location_t opening_loc; + struct yp_node_list parts; + yp_location_t closing_loc; +} yp_interpolated_string_node_t; + +// InterpolatedSymbolNode +typedef struct yp_interpolated_symbol_node { + yp_node_t base; + yp_location_t opening_loc; + struct yp_node_list parts; + yp_location_t closing_loc; +} yp_interpolated_symbol_node_t; + +// InterpolatedXStringNode +typedef struct yp_interpolated_x_string_node { + yp_node_t base; + yp_location_t opening_loc; + struct yp_node_list parts; + yp_location_t closing_loc; +} yp_interpolated_x_string_node_t; + +// KeywordHashNode +typedef struct yp_keyword_hash_node { + yp_node_t base; + struct yp_node_list elements; +} yp_keyword_hash_node_t; + +// KeywordParameterNode +typedef struct yp_keyword_parameter_node { + yp_node_t base; + yp_location_t name_loc; + struct yp_node *value; +} yp_keyword_parameter_node_t; + +// KeywordRestParameterNode +typedef struct yp_keyword_rest_parameter_node { + yp_node_t base; + yp_location_t operator_loc; + yp_location_t name_loc; +} yp_keyword_rest_parameter_node_t; + +// LambdaNode +typedef struct yp_lambda_node { + yp_node_t base; + yp_constant_id_list_t locals; + yp_location_t opening_loc; + struct yp_block_parameters_node *parameters; + struct yp_node *statements; +} yp_lambda_node_t; + +// LocalVariableOperatorAndWriteNode +typedef struct yp_local_variable_operator_and_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; + yp_constant_id_t constant_id; +} yp_local_variable_operator_and_write_node_t; + +// LocalVariableOperatorOrWriteNode +typedef struct yp_local_variable_operator_or_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; + yp_constant_id_t constant_id; +} yp_local_variable_operator_or_write_node_t; + +// LocalVariableOperatorWriteNode +typedef struct yp_local_variable_operator_write_node { + yp_node_t base; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; + yp_constant_id_t constant_id; + yp_constant_id_t operator_id; +} yp_local_variable_operator_write_node_t; + +// LocalVariableReadNode +typedef struct yp_local_variable_read_node { + yp_node_t base; + yp_constant_id_t constant_id; + uint32_t depth; +} yp_local_variable_read_node_t; + +// LocalVariableWriteNode +typedef struct yp_local_variable_write_node { + yp_node_t base; + yp_constant_id_t constant_id; + uint32_t depth; + struct yp_node *value; + yp_location_t name_loc; + yp_location_t operator_loc; +} yp_local_variable_write_node_t; + +// MatchPredicateNode +typedef struct yp_match_predicate_node { + yp_node_t base; + struct yp_node *value; + struct yp_node *pattern; + yp_location_t operator_loc; +} yp_match_predicate_node_t; + +// MatchRequiredNode +typedef struct yp_match_required_node { + yp_node_t base; + struct yp_node *value; + struct yp_node *pattern; + yp_location_t operator_loc; +} yp_match_required_node_t; + +// MissingNode +typedef struct yp_missing_node { + yp_node_t base; +} yp_missing_node_t; + +// ModuleNode +typedef struct yp_module_node { + yp_node_t base; + yp_constant_id_list_t locals; + yp_location_t module_keyword_loc; + struct yp_node *constant_path; + struct yp_node *statements; + yp_location_t end_keyword_loc; +} yp_module_node_t; + +// MultiWriteNode +typedef struct yp_multi_write_node { + yp_node_t base; + struct yp_node_list targets; + yp_location_t operator_loc; + struct yp_node *value; + yp_location_t lparen_loc; + yp_location_t rparen_loc; +} yp_multi_write_node_t; + +// NextNode +typedef struct yp_next_node { + yp_node_t base; + struct yp_arguments_node *arguments; + yp_location_t keyword_loc; +} yp_next_node_t; + +// NilNode +typedef struct yp_nil_node { + yp_node_t base; +} yp_nil_node_t; + +// NoKeywordsParameterNode +typedef struct yp_no_keywords_parameter_node { + yp_node_t base; + yp_location_t operator_loc; + yp_location_t keyword_loc; +} yp_no_keywords_parameter_node_t; + +// NumberedReferenceReadNode +typedef struct yp_numbered_reference_read_node { + yp_node_t base; +} yp_numbered_reference_read_node_t; + +// OptionalParameterNode +typedef struct yp_optional_parameter_node { + yp_node_t base; + yp_constant_id_t constant_id; + yp_location_t name_loc; + yp_location_t operator_loc; + struct yp_node *value; +} yp_optional_parameter_node_t; + +// OrNode +typedef struct yp_or_node { + yp_node_t base; + struct yp_node *left; + struct yp_node *right; + yp_location_t operator_loc; +} yp_or_node_t; + +// ParametersNode +typedef struct yp_parameters_node { + yp_node_t base; + struct yp_node_list requireds; + struct yp_node_list optionals; + struct yp_node_list posts; + struct yp_rest_parameter_node *rest; + struct yp_node_list keywords; + struct yp_node *keyword_rest; + struct yp_block_parameter_node *block; +} yp_parameters_node_t; + +// ParenthesesNode +typedef struct yp_parentheses_node { + yp_node_t base; + struct yp_node *statements; + yp_location_t opening_loc; + yp_location_t closing_loc; +} yp_parentheses_node_t; + +// PinnedExpressionNode +typedef struct yp_pinned_expression_node { + yp_node_t base; + struct yp_node *expression; + yp_location_t operator_loc; + yp_location_t lparen_loc; + yp_location_t rparen_loc; +} yp_pinned_expression_node_t; + +// PinnedVariableNode +typedef struct yp_pinned_variable_node { + yp_node_t base; + struct yp_node *variable; + yp_location_t operator_loc; +} yp_pinned_variable_node_t; + +// PostExecutionNode +typedef struct yp_post_execution_node { + yp_node_t base; + struct yp_statements_node *statements; + yp_location_t keyword_loc; + yp_location_t opening_loc; + yp_location_t closing_loc; +} yp_post_execution_node_t; + +// PreExecutionNode +typedef struct yp_pre_execution_node { + yp_node_t base; + struct yp_statements_node *statements; + yp_location_t keyword_loc; + yp_location_t opening_loc; + yp_location_t closing_loc; +} yp_pre_execution_node_t; + +// ProgramNode +typedef struct yp_program_node { + yp_node_t base; + yp_constant_id_list_t locals; + struct yp_statements_node *statements; +} yp_program_node_t; + +// RangeNode +typedef struct yp_range_node { + yp_node_t base; + struct yp_node *left; + struct yp_node *right; + yp_location_t operator_loc; + uint32_t flags; +} yp_range_node_t; + +// RationalNode +typedef struct yp_rational_node { + yp_node_t base; + struct yp_node *numeric; +} yp_rational_node_t; + +// RedoNode +typedef struct yp_redo_node { + yp_node_t base; +} yp_redo_node_t; + +// RegularExpressionNode +typedef struct yp_regular_expression_node { + yp_node_t base; + yp_location_t opening_loc; + yp_location_t content_loc; + yp_location_t closing_loc; + yp_string_t unescaped; + uint32_t flags; +} yp_regular_expression_node_t; + +// RequiredDestructuredParameterNode +typedef struct yp_required_destructured_parameter_node { + yp_node_t base; + struct yp_node_list parameters; + yp_location_t opening_loc; + yp_location_t closing_loc; +} yp_required_destructured_parameter_node_t; + +// RequiredParameterNode +typedef struct yp_required_parameter_node { + yp_node_t base; + yp_constant_id_t constant_id; +} yp_required_parameter_node_t; + +// RescueModifierNode +typedef struct yp_rescue_modifier_node { + yp_node_t base; + struct yp_node *expression; + yp_location_t keyword_loc; + struct yp_node *rescue_expression; +} yp_rescue_modifier_node_t; + +// RescueNode +typedef struct yp_rescue_node { + yp_node_t base; + yp_location_t keyword_loc; + struct yp_node_list exceptions; + yp_location_t operator_loc; + struct yp_node *exception; + struct yp_statements_node *statements; + struct yp_rescue_node *consequent; +} yp_rescue_node_t; + +// RestParameterNode +typedef struct yp_rest_parameter_node { + yp_node_t base; + yp_location_t operator_loc; + yp_location_t name_loc; +} yp_rest_parameter_node_t; + +// RetryNode +typedef struct yp_retry_node { + yp_node_t base; +} yp_retry_node_t; + +// ReturnNode +typedef struct yp_return_node { + yp_node_t base; + yp_location_t keyword_loc; + struct yp_arguments_node *arguments; +} yp_return_node_t; + +// SelfNode +typedef struct yp_self_node { + yp_node_t base; +} yp_self_node_t; + +// SingletonClassNode +typedef struct yp_singleton_class_node { + yp_node_t base; + yp_constant_id_list_t locals; + yp_location_t class_keyword_loc; + yp_location_t operator_loc; + struct yp_node *expression; + struct yp_node *statements; + yp_location_t end_keyword_loc; +} yp_singleton_class_node_t; + +// SourceEncodingNode +typedef struct yp_source_encoding_node { + yp_node_t base; +} yp_source_encoding_node_t; + +// SourceFileNode +typedef struct yp_source_file_node { + yp_node_t base; + yp_string_t filepath; +} yp_source_file_node_t; + +// SourceLineNode +typedef struct yp_source_line_node { + yp_node_t base; +} yp_source_line_node_t; + +// SplatNode +typedef struct yp_splat_node { + yp_node_t base; + yp_location_t operator_loc; + struct yp_node *expression; +} yp_splat_node_t; + +// StatementsNode +typedef struct yp_statements_node { + yp_node_t base; + struct yp_node_list body; +} yp_statements_node_t; + +// StringConcatNode +typedef struct yp_string_concat_node { + yp_node_t base; + struct yp_node *left; + struct yp_node *right; +} yp_string_concat_node_t; + +// StringNode +typedef struct yp_string_node { + yp_node_t base; + yp_location_t opening_loc; + yp_location_t content_loc; + yp_location_t closing_loc; + yp_string_t unescaped; +} yp_string_node_t; + +// SuperNode +typedef struct yp_super_node { + yp_node_t base; + yp_location_t keyword_loc; + yp_location_t lparen_loc; + struct yp_arguments_node *arguments; + yp_location_t rparen_loc; + struct yp_block_node *block; +} yp_super_node_t; + +// SymbolNode +typedef struct yp_symbol_node { + yp_node_t base; + yp_location_t opening_loc; + yp_location_t value_loc; + yp_location_t closing_loc; + yp_string_t unescaped; +} yp_symbol_node_t; + +// TrueNode +typedef struct yp_true_node { + yp_node_t base; +} yp_true_node_t; + +// UndefNode +typedef struct yp_undef_node { + yp_node_t base; + struct yp_node_list names; + yp_location_t keyword_loc; +} yp_undef_node_t; + +// UnlessNode +typedef struct yp_unless_node { + yp_node_t base; + yp_location_t keyword_loc; + struct yp_node *predicate; + struct yp_statements_node *statements; + struct yp_else_node *consequent; + yp_location_t end_keyword_loc; +} yp_unless_node_t; + +// UntilNode +typedef struct yp_until_node { + yp_node_t base; + yp_location_t keyword_loc; + struct yp_node *predicate; + struct yp_statements_node *statements; +} yp_until_node_t; + +// WhenNode +typedef struct yp_when_node { + yp_node_t base; + yp_location_t keyword_loc; + struct yp_node_list conditions; + struct yp_statements_node *statements; +} yp_when_node_t; + +// WhileNode +typedef struct yp_while_node { + yp_node_t base; + yp_location_t keyword_loc; + struct yp_node *predicate; + struct yp_statements_node *statements; +} yp_while_node_t; + +// XStringNode +typedef struct yp_x_string_node { + yp_node_t base; + yp_location_t opening_loc; + yp_location_t content_loc; + yp_location_t closing_loc; + yp_string_t unescaped; +} yp_x_string_node_t; + +// YieldNode +typedef struct yp_yield_node { + yp_node_t base; + yp_location_t keyword_loc; + yp_location_t lparen_loc; + struct yp_arguments_node *arguments; + yp_location_t rparen_loc; +} yp_yield_node_t; + +// CallNodeFlags +typedef enum { + YP_CALL_NODE_FLAGS_SAFE_NAVIGATION = 1 << 0, +} yp_call_node_flags_t; + +// RangeNodeFlags +typedef enum { + YP_RANGE_NODE_FLAGS_EXCLUDE_END = 1 << 0, +} yp_range_node_flags_t; + +// RegularExpressionFlags +typedef enum { + YP_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE = 1 << 0, + YP_REGULAR_EXPRESSION_FLAGS_MULTI_LINE = 1 << 1, + YP_REGULAR_EXPRESSION_FLAGS_EXTENDED = 1 << 2, + YP_REGULAR_EXPRESSION_FLAGS_EUC_JP = 1 << 3, + YP_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT = 1 << 4, + YP_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J = 1 << 5, + YP_REGULAR_EXPRESSION_FLAGS_UTF_8 = 1 << 6, + YP_REGULAR_EXPRESSION_FLAGS_ONCE = 1 << 7, +} yp_regular_expression_flags_t; + +#endif // YARP_AST_H diff --git a/src/main/c/yarp/include/yarp/defines.h b/src/main/c/yarp/include/yarp/defines.h new file mode 100644 index 000000000000..19a71b02faa0 --- /dev/null +++ b/src/main/c/yarp/include/yarp/defines.h @@ -0,0 +1,51 @@ +#ifndef YARP_DEFINES_H +#define YARP_DEFINES_H + +// This file should be included first by any *.h or *.c in YARP + +#include "yarp/config.h" + +#include +#include +#include +#include +#include + +// YP_EXPORTED_FUNCTION +#if defined(YP_STATIC) +# define YP_EXPORTED_FUNCTION +#elif defined(_WIN32) +# define YP_EXPORTED_FUNCTION __declspec(dllexport) extern +#else +# ifndef YP_EXPORTED_FUNCTION +# ifndef RUBY_FUNC_EXPORTED +# define YP_EXPORTED_FUNCTION __attribute__((__visibility__("default"))) extern +# else +# define YP_EXPORTED_FUNCTION RUBY_FUNC_EXPORTED +# endif +# endif +#endif + +// YP_ATTRIBUTE_UNUSED +#if defined(__GNUC__) +# define YP_ATTRIBUTE_UNUSED __attribute__((unused)) +#else +# define YP_ATTRIBUTE_UNUSED +#endif + +// strncasecmp +#if !defined(HAVE_STRNCASECMP) && !defined(strncasecmp) + // In case strncasecmp isn't present on the system, we provide our own. + int yp_strncasecmp(const char *string1, const char *string2, size_t length); +# define strncasecmp yp_strncasecmp +#endif + +// snprintf +#if !defined(HAVE_SNPRINTF) && !defined(snprintf) + // In case snprintf isn't present on the system, we provide our own that + // simply forwards to the less-safe sprintf. + int yp_snprintf(char *dest, YP_ATTRIBUTE_UNUSED size_t size, const char *format, ...); +# define snprintf yp_snprintf +#endif + +#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/diagnostic.h b/src/main/c/yarp/include/yarp/diagnostic.h similarity index 57% rename from src/main/c/yarp/src/yarp/include/yarp/diagnostic.h rename to src/main/c/yarp/include/yarp/diagnostic.h index a4c29db24d6d..bcbee5380ca9 100644 --- a/src/main/c/yarp/src/yarp/include/yarp/diagnostic.h +++ b/src/main/c/yarp/include/yarp/diagnostic.h @@ -2,25 +2,23 @@ #define YARP_DIAGNOSTIC_H #include "yarp/defines.h" +#include "yarp/util/yp_list.h" +#include #include -#include "yarp/util/yp_list.h" - // This struct represents a diagnostic found during parsing. typedef struct { - yp_list_node_t node; - const char *start; - const char *end; - const char *message; + yp_list_node_t node; + const char *start; + const char *end; + const char *message; } yp_diagnostic_t; // Append a diagnostic to the given list of diagnostics. -void -yp_diagnostic_list_append(yp_list_t *list, const char *start, const char *end, const char *message); +bool yp_diagnostic_list_append(yp_list_t *list, const char *start, const char *end, const char *message); // Deallocate the internal state of the given diagnostic list. -void -yp_diagnostic_list_free(yp_list_t *list); +void yp_diagnostic_list_free(yp_list_t *list); #endif diff --git a/src/main/c/yarp/include/yarp/enc/yp_encoding.h b/src/main/c/yarp/include/yarp/enc/yp_encoding.h new file mode 100644 index 000000000000..24714bd41f85 --- /dev/null +++ b/src/main/c/yarp/include/yarp/enc/yp_encoding.h @@ -0,0 +1,94 @@ +#ifndef YARP_ENCODING_H +#define YARP_ENCODING_H + +#include "yarp/defines.h" + +#include +#include +#include + +// This struct defines the functions necessary to implement the encoding +// interface so we can determine how many bytes the subsequent character takes. +// Each callback should return the number of bytes, or 0 if the next bytes are +// invalid for the encoding and type. +typedef struct { + // Return the number of bytes that the next character takes if it is valid + // in the encoding. + size_t (*char_width)(const char *c); + + // Return the number of bytes that the next character takes if it is valid + // in the encoding and is alphabetical. + size_t (*alpha_char)(const char *c); + + // Return the number of bytes that the next character takes if it is valid + // in the encoding and is alphanumeric. + size_t (*alnum_char)(const char *c); + + // Return true if the next character is valid in the encoding and is an + // uppercase character. + bool (*isupper_char)(const char *c); + + // The name of the encoding. This should correspond to a value that can be + // passed to Encoding.find in Ruby. + const char *name; + + // Return true if the encoding is a multibyte encoding. + bool multibyte; +} yp_encoding_t; + +// These bits define the location of each bit of metadata within the various +// lookup tables that are used to determine the properties of a character. +#define YP_ENCODING_ALPHABETIC_BIT 1 << 0 +#define YP_ENCODING_ALPHANUMERIC_BIT 1 << 1 +#define YP_ENCODING_UPPERCASE_BIT 1 << 2 + +// The function is shared between all of the encodings that use single bytes to +// represent characters. They don't have need of a dynamic function to determine +// their width. +size_t yp_encoding_single_char_width(YP_ATTRIBUTE_UNUSED const char *c); + +// These functions are reused by some other encodings, so they are defined here +// so they can be shared. +size_t yp_encoding_ascii_alpha_char(const char *c); +size_t yp_encoding_ascii_alnum_char(const char *c); +bool yp_encoding_ascii_isupper_char(const char *c); + +// These functions are shared between the actual encoding and the fast path in +// the parser so they need to be internally visible. +size_t yp_encoding_utf_8_alpha_char(const char *c); +size_t yp_encoding_utf_8_alnum_char(const char *c); + +// This lookup table is referenced in both the UTF-8 encoding file and the +// parser directly in order to speed up the default encoding processing. +extern unsigned char yp_encoding_unicode_table[256]; + +// These are the encodings that are supported by the parser. They are defined in +// their own files in the src/enc directory. +extern yp_encoding_t yp_encoding_ascii; +extern yp_encoding_t yp_encoding_ascii_8bit; +extern yp_encoding_t yp_encoding_big5; +extern yp_encoding_t yp_encoding_euc_jp; +extern yp_encoding_t yp_encoding_gbk; +extern yp_encoding_t yp_encoding_iso_8859_1; +extern yp_encoding_t yp_encoding_iso_8859_2; +extern yp_encoding_t yp_encoding_iso_8859_3; +extern yp_encoding_t yp_encoding_iso_8859_4; +extern yp_encoding_t yp_encoding_iso_8859_5; +extern yp_encoding_t yp_encoding_iso_8859_6; +extern yp_encoding_t yp_encoding_iso_8859_7; +extern yp_encoding_t yp_encoding_iso_8859_8; +extern yp_encoding_t yp_encoding_iso_8859_9; +extern yp_encoding_t yp_encoding_iso_8859_10; +extern yp_encoding_t yp_encoding_iso_8859_11; +extern yp_encoding_t yp_encoding_iso_8859_13; +extern yp_encoding_t yp_encoding_iso_8859_14; +extern yp_encoding_t yp_encoding_iso_8859_15; +extern yp_encoding_t yp_encoding_iso_8859_16; +extern yp_encoding_t yp_encoding_koi8_r; +extern yp_encoding_t yp_encoding_shift_jis; +extern yp_encoding_t yp_encoding_utf_8; +extern yp_encoding_t yp_encoding_windows_31j; +extern yp_encoding_t yp_encoding_windows_1251; +extern yp_encoding_t yp_encoding_windows_1252; + +#endif diff --git a/src/main/c/yarp/include/yarp/node.h b/src/main/c/yarp/include/yarp/node.h new file mode 100644 index 000000000000..f75e03ba1045 --- /dev/null +++ b/src/main/c/yarp/include/yarp/node.h @@ -0,0 +1,33 @@ +#ifndef YARP_NODE_H +#define YARP_NODE_H + +#include "yarp/defines.h" +#include "yarp/parser.h" + +// Append a token to the given list. +void yp_location_list_append(yp_location_list_t *list, const yp_token_t *token); + +// Append a new node onto the end of the node list. +void yp_node_list_append(yp_node_list_t *list, yp_node_t *node); + +// Clear the node but preserves the location. +void yp_node_clear(yp_node_t *node); + +// Deallocate a node and all of its children. +YP_EXPORTED_FUNCTION void yp_node_destroy(yp_parser_t *parser, struct yp_node *node); + +// This struct stores the information gathered by the yp_node_memsize function. +// It contains both the memory footprint and additionally metadata about the +// shape of the tree. +typedef struct { + size_t memsize; + size_t node_count; +} yp_memsize_t; + +// Calculates the memory footprint of a given node. +YP_EXPORTED_FUNCTION void yp_node_memsize(yp_node_t *node, yp_memsize_t *memsize); + +#define YP_EMPTY_NODE_LIST ((yp_node_list_t) { .nodes = NULL, .size = 0, .capacity = 0 }) +#define YP_EMPTY_LOCATION_LIST ((yp_location_list_t) { .locations = NULL, .size = 0, .capacity = 0 }) + +#endif // YARP_NODE_H diff --git a/src/main/c/yarp/src/yarp/include/yarp/pack.h b/src/main/c/yarp/include/yarp/pack.h similarity index 53% rename from src/main/c/yarp/src/yarp/include/yarp/pack.h rename to src/main/c/yarp/include/yarp/pack.h index ff2f971b260a..9d6204bf7b74 100644 --- a/src/main/c/yarp/src/yarp/include/yarp/pack.h +++ b/src/main/c/yarp/include/yarp/pack.h @@ -3,91 +3,91 @@ #include "yarp/defines.h" -#include #include +#include typedef enum yp_pack_version { - YP_PACK_VERSION_3_2_0 + YP_PACK_VERSION_3_2_0 } yp_pack_version; typedef enum yp_pack_variant { - YP_PACK_VARIANT_PACK, - YP_PACK_VARIANT_UNPACK + YP_PACK_VARIANT_PACK, + YP_PACK_VARIANT_UNPACK } yp_pack_variant; typedef enum yp_pack_type { - YP_PACK_SPACE, - YP_PACK_COMMENT, - YP_PACK_INTEGER, - YP_PACK_UTF8, - YP_PACK_BER, - YP_PACK_FLOAT, - YP_PACK_STRING_SPACE_PADDED, - YP_PACK_STRING_NULL_PADDED, - YP_PACK_STRING_NULL_TERMINATED, - YP_PACK_STRING_MSB, - YP_PACK_STRING_LSB, - YP_PACK_STRING_HEX_HIGH, - YP_PACK_STRING_HEX_LOW, - YP_PACK_STRING_UU, - YP_PACK_STRING_MIME, - YP_PACK_STRING_BASE64, - YP_PACK_STRING_FIXED, - YP_PACK_STRING_POINTER, - YP_PACK_MOVE, - YP_PACK_BACK, - YP_PACK_NULL, - YP_PACK_END + YP_PACK_SPACE, + YP_PACK_COMMENT, + YP_PACK_INTEGER, + YP_PACK_UTF8, + YP_PACK_BER, + YP_PACK_FLOAT, + YP_PACK_STRING_SPACE_PADDED, + YP_PACK_STRING_NULL_PADDED, + YP_PACK_STRING_NULL_TERMINATED, + YP_PACK_STRING_MSB, + YP_PACK_STRING_LSB, + YP_PACK_STRING_HEX_HIGH, + YP_PACK_STRING_HEX_LOW, + YP_PACK_STRING_UU, + YP_PACK_STRING_MIME, + YP_PACK_STRING_BASE64, + YP_PACK_STRING_FIXED, + YP_PACK_STRING_POINTER, + YP_PACK_MOVE, + YP_PACK_BACK, + YP_PACK_NULL, + YP_PACK_END } yp_pack_type; typedef enum yp_pack_signed { - YP_PACK_UNSIGNED, - YP_PACK_SIGNED, - YP_PACK_SIGNED_NA + YP_PACK_UNSIGNED, + YP_PACK_SIGNED, + YP_PACK_SIGNED_NA } yp_pack_signed; typedef enum yp_pack_endian { - YP_PACK_AGNOSTIC_ENDIAN, - YP_PACK_LITTLE_ENDIAN, // aka 'VAX', or 'V' - YP_PACK_BIG_ENDIAN, // aka 'network', or 'N' - YP_PACK_NATIVE_ENDIAN, - YP_PACK_ENDIAN_NA + YP_PACK_AGNOSTIC_ENDIAN, + YP_PACK_LITTLE_ENDIAN, // aka 'VAX', or 'V' + YP_PACK_BIG_ENDIAN, // aka 'network', or 'N' + YP_PACK_NATIVE_ENDIAN, + YP_PACK_ENDIAN_NA } yp_pack_endian; typedef enum yp_pack_size { - YP_PACK_SIZE_SHORT, - YP_PACK_SIZE_INT, - YP_PACK_SIZE_LONG, - YP_PACK_SIZE_LONG_LONG, - YP_PACK_SIZE_8, - YP_PACK_SIZE_16, - YP_PACK_SIZE_32, - YP_PACK_SIZE_64, - YP_PACK_SIZE_P, - YP_PACK_SIZE_NA + YP_PACK_SIZE_SHORT, + YP_PACK_SIZE_INT, + YP_PACK_SIZE_LONG, + YP_PACK_SIZE_LONG_LONG, + YP_PACK_SIZE_8, + YP_PACK_SIZE_16, + YP_PACK_SIZE_32, + YP_PACK_SIZE_64, + YP_PACK_SIZE_P, + YP_PACK_SIZE_NA } yp_pack_size; typedef enum yp_pack_length_type { - YP_PACK_LENGTH_FIXED, - YP_PACK_LENGTH_MAX, - YP_PACK_LENGTH_RELATIVE, // special case for unpack @* - YP_PACK_LENGTH_NA + YP_PACK_LENGTH_FIXED, + YP_PACK_LENGTH_MAX, + YP_PACK_LENGTH_RELATIVE, // special case for unpack @* + YP_PACK_LENGTH_NA } yp_pack_length_type; typedef enum yp_pack_encoding { - YP_PACK_ENCODING_START, - YP_PACK_ENCODING_ASCII_8BIT, - YP_PACK_ENCODING_US_ASCII, - YP_PACK_ENCODING_UTF_8 + YP_PACK_ENCODING_START, + YP_PACK_ENCODING_ASCII_8BIT, + YP_PACK_ENCODING_US_ASCII, + YP_PACK_ENCODING_UTF_8 } yp_pack_encoding; typedef enum yp_pack_result { - YP_PACK_OK, - YP_PACK_ERROR_UNSUPPORTED_DIRECTIVE, - YP_PACK_ERROR_UNKNOWN_DIRECTIVE, - YP_PACK_ERROR_LENGTH_TOO_BIG, - YP_PACK_ERROR_BANG_NOT_ALLOWED, - YP_PACK_ERROR_DOUBLE_ENDIAN + YP_PACK_OK, + YP_PACK_ERROR_UNSUPPORTED_DIRECTIVE, + YP_PACK_ERROR_UNKNOWN_DIRECTIVE, + YP_PACK_ERROR_LENGTH_TOO_BIG, + YP_PACK_ERROR_BANG_NOT_ALLOWED, + YP_PACK_ERROR_DOUBLE_ENDIAN } yp_pack_result; // Parse a single directive from a pack or unpack format string. @@ -120,24 +120,22 @@ typedef enum yp_pack_result { // // Notes: // Consult Ruby documentation for the meaning of directives. -__attribute__((__visibility__("default"))) extern yp_pack_result +YP_EXPORTED_FUNCTION yp_pack_result yp_pack_parse( - __attribute__((unused)) yp_pack_version version, - yp_pack_variant variant, - const char **format, - const char *format_end, - yp_pack_type *type, - yp_pack_signed *signed_type, - yp_pack_endian *endian, - yp_pack_size *size, - yp_pack_length_type *length_type, - uint64_t *length, - yp_pack_encoding *encoding + yp_pack_variant variant_arg, + const char **format, + const char *format_end, + yp_pack_type *type, + yp_pack_signed *signed_type, + yp_pack_endian *endian, + yp_pack_size *size, + yp_pack_length_type *length_type, + uint64_t *length, + yp_pack_encoding *encoding ); // YARP abstracts sizes away from the native system - this converts an abstract // size to a native size. -__attribute__((__visibility__("default"))) extern size_t -yp_size_to_native(yp_pack_size size); +YP_EXPORTED_FUNCTION size_t yp_size_to_native(yp_pack_size size); #endif diff --git a/src/main/c/yarp/include/yarp/parser.h b/src/main/c/yarp/include/yarp/parser.h new file mode 100644 index 000000000000..a23f88608497 --- /dev/null +++ b/src/main/c/yarp/include/yarp/parser.h @@ -0,0 +1,398 @@ +#ifndef YARP_PARSER_H +#define YARP_PARSER_H + +#include "yarp/ast.h" +#include "yarp/defines.h" +#include "yarp/enc/yp_encoding.h" +#include "yarp/util/yp_constant_pool.h" +#include "yarp/util/yp_list.h" +#include "yarp/util/yp_newline_list.h" +#include "yarp/util/yp_state_stack.h" + +#include + +// This enum provides various bits that represent different kinds of states that +// the lexer can track. This is used to determine which kind of token to return +// based on the context of the parser. +typedef enum { + YP_LEX_STATE_BIT_BEG, + YP_LEX_STATE_BIT_END, + YP_LEX_STATE_BIT_ENDARG, + YP_LEX_STATE_BIT_ENDFN, + YP_LEX_STATE_BIT_ARG, + YP_LEX_STATE_BIT_CMDARG, + YP_LEX_STATE_BIT_MID, + YP_LEX_STATE_BIT_FNAME, + YP_LEX_STATE_BIT_DOT, + YP_LEX_STATE_BIT_CLASS, + YP_LEX_STATE_BIT_LABEL, + YP_LEX_STATE_BIT_LABELED, + YP_LEX_STATE_BIT_FITEM +} yp_lex_state_bit_t; + +// This enum combines the various bits from the above enum into individual +// values that represent the various states of the lexer. +typedef enum { + YP_LEX_STATE_NONE = 0, + YP_LEX_STATE_BEG = (1 << YP_LEX_STATE_BIT_BEG), + YP_LEX_STATE_END = (1 << YP_LEX_STATE_BIT_END), + YP_LEX_STATE_ENDARG = (1 << YP_LEX_STATE_BIT_ENDARG), + YP_LEX_STATE_ENDFN = (1 << YP_LEX_STATE_BIT_ENDFN), + YP_LEX_STATE_ARG = (1 << YP_LEX_STATE_BIT_ARG), + YP_LEX_STATE_CMDARG = (1 << YP_LEX_STATE_BIT_CMDARG), + YP_LEX_STATE_MID = (1 << YP_LEX_STATE_BIT_MID), + YP_LEX_STATE_FNAME = (1 << YP_LEX_STATE_BIT_FNAME), + YP_LEX_STATE_DOT = (1 << YP_LEX_STATE_BIT_DOT), + YP_LEX_STATE_CLASS = (1 << YP_LEX_STATE_BIT_CLASS), + YP_LEX_STATE_LABEL = (1 << YP_LEX_STATE_BIT_LABEL), + YP_LEX_STATE_LABELED = (1 << YP_LEX_STATE_BIT_LABELED), + YP_LEX_STATE_FITEM = (1 << YP_LEX_STATE_BIT_FITEM), + YP_LEX_STATE_BEG_ANY = YP_LEX_STATE_BEG | YP_LEX_STATE_MID | YP_LEX_STATE_CLASS, + YP_LEX_STATE_ARG_ANY = YP_LEX_STATE_ARG | YP_LEX_STATE_CMDARG, + YP_LEX_STATE_END_ANY = YP_LEX_STATE_END | YP_LEX_STATE_ENDARG | YP_LEX_STATE_ENDFN +} yp_lex_state_t; + +typedef enum { + YP_HEREDOC_QUOTE_NONE, + YP_HEREDOC_QUOTE_SINGLE = '\'', + YP_HEREDOC_QUOTE_DOUBLE = '"', + YP_HEREDOC_QUOTE_BACKTICK = '`', +} yp_heredoc_quote_t; + +typedef enum { + YP_HEREDOC_INDENT_NONE, + YP_HEREDOC_INDENT_DASH, + YP_HEREDOC_INDENT_TILDE, +} yp_heredoc_indent_t; + +// When lexing Ruby source, the lexer has a small amount of state to tell which +// kind of token it is currently lexing. For example, when we find the start of +// a string, the first token that we return is a TOKEN_STRING_BEGIN token. After +// that the lexer is now in the YP_LEX_STRING mode, and will return tokens that +// are found as part of a string. +typedef struct yp_lex_mode { + enum { + // This state is used when any given token is being lexed. + YP_LEX_DEFAULT, + + // This state is used when we're lexing as normal but inside an embedded + // expression of a string. + YP_LEX_EMBEXPR, + + // This state is used when we're lexing a variable that is embedded + // directly inside of a string with the # shorthand. + YP_LEX_EMBVAR, + + // This state is used when you are inside the content of a heredoc. + YP_LEX_HEREDOC, + + // This state is used when we are lexing a list of tokens, as in a %w + // word list literal or a %i symbol list literal. + YP_LEX_LIST, + + // This state is used when a regular expression has been begun and we + // are looking for the terminator. + YP_LEX_REGEXP, + + // This state is used when we are lexing a string or a string-like + // token, as in string content with either quote or an xstring. + YP_LEX_STRING, + + // you lexed a number with extra information attached + YP_LEX_NUMERIC, + } mode; + + union { + struct { + // This keeps track of the nesting level of the list. + size_t nesting; + + // Whether or not interpolation is allowed in this list. + bool interpolation; + + // When lexing a list, it takes into account balancing the + // terminator if the terminator is one of (), [], {}, or <>. + char incrementor; + + // This is the terminator of the list literal. + char terminator; + + // This is the character set that should be used to delimit the + // tokens within the list. + char breakpoints[11]; + } list; + + struct { + // This keeps track of the nesting level of the regular expression. + size_t nesting; + + // When lexing a regular expression, it takes into account balancing + // the terminator if the terminator is one of (), [], {}, or <>. + char incrementor; + + // This is the terminator of the regular expression. + char terminator; + + // This is the character set that should be used to delimit the + // tokens within the regular expression. + char breakpoints[6]; + } regexp; + + struct { + // This keeps track of the nesting level of the string. + size_t nesting; + + // Whether or not interpolation is allowed in this string. + bool interpolation; + + // Whether or not at the end of the string we should allow a :, + // which would indicate this was a dynamic symbol instead of a + // string. + bool label_allowed; + + // When lexing a string, it takes into account balancing the + // terminator if the terminator is one of (), [], {}, or <>. + char incrementor; + + // This is the terminator of the string. It is typically either a + // single or double quote. + char terminator; + + // This is the character set that should be used to delimit the + // tokens within the string. + char breakpoints[6]; + } string; + + struct { + yp_token_type_t type; + const char *start; + const char *end; + } numeric; + + struct { + // These pointers point to the beginning and end of the heredoc + // identifier. + const char *ident_start; + size_t ident_length; + + yp_heredoc_quote_t quote; + yp_heredoc_indent_t indent; + + // This is the pointer to the character where lexing should resume + // once the heredoc has been completely processed. + const char *next_start; + } heredoc; + } as; + + // The previous lex state so that it knows how to pop. + struct yp_lex_mode *prev; +} yp_lex_mode_t; + +// We pre-allocate a certain number of lex states in order to avoid having to +// call malloc too many times while parsing. You really shouldn't need more than +// this because you only really nest deeply when doing string interpolation. +#define YP_LEX_STACK_SIZE 4 + +// A forward declaration since our error handler struct accepts a parser for +// each of its function calls. +typedef struct yp_parser yp_parser_t; + +// While parsing, we keep track of a stack of contexts. This is helpful for +// error recovery so that we can pop back to a previous context when we hit a +// token that is understood by a parent context but not by the current context. +typedef enum { + YP_CONTEXT_BEGIN, // a begin statement + YP_CONTEXT_BLOCK_BRACES, // expressions in block arguments using braces + YP_CONTEXT_BLOCK_KEYWORDS, // expressions in block arguments using do..end + YP_CONTEXT_CASE_WHEN, // a case when statements + YP_CONTEXT_CASE_IN, // a case in statements + YP_CONTEXT_CLASS, // a class declaration + YP_CONTEXT_DEF, // a method definition + YP_CONTEXT_DEF_PARAMS, // a method definition's parameters + YP_CONTEXT_DEFAULT_PARAMS, // a method definition's default parameter + YP_CONTEXT_ELSE, // an else clause + YP_CONTEXT_ELSIF, // an elsif clause + YP_CONTEXT_EMBEXPR, // an interpolated expression + YP_CONTEXT_ENSURE, // an ensure statement + YP_CONTEXT_FOR, // a for loop + YP_CONTEXT_IF, // an if statement + YP_CONTEXT_LAMBDA_BRACES, // a lambda expression with braces + YP_CONTEXT_LAMBDA_DO_END, // a lambda expression with do..end + YP_CONTEXT_MAIN, // the top level context + YP_CONTEXT_MODULE, // a module declaration + YP_CONTEXT_PARENS, // a parenthesized expression + YP_CONTEXT_POSTEXE, // an END block + YP_CONTEXT_PREDICATE, // a predicate inside an if/elsif/unless statement + YP_CONTEXT_PREEXE, // a BEGIN block + YP_CONTEXT_RESCUE_ELSE, // a rescue else statement + YP_CONTEXT_RESCUE, // a rescue statement + YP_CONTEXT_SCLASS, // a singleton class definition + YP_CONTEXT_UNLESS, // an unless statement + YP_CONTEXT_UNTIL, // an until statement + YP_CONTEXT_WHILE, // a while statement +} yp_context_t; + +// This is a node in a linked list of contexts. +typedef struct yp_context_node { + yp_context_t context; + struct yp_context_node *prev; +} yp_context_node_t; + +// This is the type of a comment that we've found while parsing. +typedef enum { + YP_COMMENT_INLINE, + YP_COMMENT_EMBDOC, + YP_COMMENT___END__ +} yp_comment_type_t; + +// This is a node in the linked list of comments that we've found while parsing. +typedef struct yp_comment { + yp_list_node_t node; + const char *start; + const char *end; + yp_comment_type_t type; +} yp_comment_t; + +// When the encoding that is being used to parse the source is changed by YARP, +// we provide the ability here to call out to a user-defined function. +typedef void (*yp_encoding_changed_callback_t)(yp_parser_t *parser); + +// When an encoding is encountered that isn't understood by YARP, we provide +// the ability here to call out to a user-defined function to get an encoding +// struct. If the function returns something that isn't NULL, we set that to +// our encoding and use it to parse identifiers. +typedef yp_encoding_t *(*yp_encoding_decode_callback_t)(yp_parser_t *parser, const char *name, size_t width); + +// When you are lexing through a file, the lexer needs all of the information +// that the parser additionally provides (for example, the local table). So if +// you want to properly lex Ruby, you need to actually lex it in the context of +// the parser. In order to provide this functionality, we optionally allow a +// struct to be attached to the parser that calls back out to a user-provided +// callback when each token is lexed. +typedef struct { + // This opaque pointer is used to provide whatever information the user + // deemed necessary to the callback. In our case we use it to pass the array + // that the tokens get appended into. + void *data; + + // This is the callback that is called when a token is lexed. It is passed + // the opaque data pointer, the parser, and the token that was lexed. + void (*callback)(void *data, yp_parser_t *parser, yp_token_t *token); +} yp_lex_callback_t; + +// This struct represents a node in a linked list of scopes. Some scopes can see +// into their parent scopes, while others cannot. +typedef struct yp_scope { + // The IDs of the locals in the given scope. + yp_constant_id_list_t locals; + + // A boolean indicating whether or not this scope can see into its parent. + // If closed is true, then the scope cannot see into its parent. + bool closed; + + // A pointer to the previous scope in the linked list. + struct yp_scope *previous; +} yp_scope_t; + +// This struct represents the overall parser. It contains a reference to the +// source file, as well as pointers that indicate where in the source it's +// currently parsing. It also contains the most recent and current token that +// it's considering. +struct yp_parser { + yp_lex_state_t lex_state; // the current state of the lexer + bool command_start; // whether or not we're at the beginning of a command + int enclosure_nesting; // tracks the current nesting of (), [], and {} + + // Used to temporarily track the nesting of enclosures to determine if a { + // is the beginning of a lambda following the parameters of a lambda. + int lambda_enclosure_nesting; + + // Used to track the nesting of braces to ensure we get the correct value + // when we are interpolating blocks with braces. + int brace_nesting; + + // the stack used to determine if a do keyword belongs to the predicate of a + // while, until, or for loop + yp_state_stack_t do_loop_stack; + + // the stack used to determine if a do keyword belongs to the beginning of a + // block + yp_state_stack_t accepts_block_stack; + + struct { + yp_lex_mode_t *current; // the current mode of the lexer + yp_lex_mode_t stack[YP_LEX_STACK_SIZE]; // the stack of lexer modes + size_t index; // the current index into the lexer mode stack + } lex_modes; + + const char *start; // the pointer to the start of the source + const char *end; // the pointer to the end of the source + yp_token_t previous; // the previous token we were considering + yp_token_t current; // the current token we're considering + + // This is a special field set on the parser when we need the parser to jump + // to a specific location when lexing the next token, as opposed to just + // using the end of the previous token. Normally this is NULL. + const char *next_start; + + // This field indicates the end of a heredoc whose identifier was found on + // the current line. If another heredoc is found on the same line, then this + // will be moved forward to the end of that heredoc. If no heredocs are + // found on a line then this is NULL. + const char *heredoc_end; + + yp_list_t comment_list; // the list of comments that have been found while parsing + yp_list_t warning_list; // the list of warnings that have been found while parsing + yp_list_t error_list; // the list of errors that have been found while parsing + yp_scope_t *current_scope; // the current local scope + + yp_context_node_t *current_context; // the current parsing context + bool recovering; // whether or not we're currently recovering from a syntax error + + // The encoding functions for the current file is attached to the parser as + // it's parsing so that it can change with a magic comment. + yp_encoding_t encoding; + + // Whether or not the encoding has been changed by a magic comment. We use + // this to provide a fast path for the lexer instead of going through the + // function pointer. + bool encoding_changed; + + // When the encoding that is being used to parse the source is changed by + // YARP, we provide the ability here to call out to a user-defined function. + yp_encoding_changed_callback_t encoding_changed_callback; + + // When an encoding is encountered that isn't understood by YARP, we provide + // the ability here to call out to a user-defined function to get an + // encoding struct. If the function returns something that isn't NULL, we + // set that to our encoding and use it to parse identifiers. + yp_encoding_decode_callback_t encoding_decode_callback; + + // This pointer indicates where a comment must start if it is to be + // considered an encoding comment. + const char *encoding_comment_start; + + // This is an optional callback that can be attached to the parser that will + // be called whenever a new token is lexed by the parser. + yp_lex_callback_t *lex_callback; + + // This flag indicates that we are currently parsing a pattern matching + // expression and impacts that calculation of newlines. + bool pattern_matching_newlines; + + // This flag indicates that we are currently parsing a keyword argument. + bool in_keyword_arg; + + // This is the path of the file being parsed + // We use the filepath when constructing SourceFileNodes + yp_string_t filepath_string; + + // This constant pool keeps all of the constants defined throughout the file + // so that we can reference them later. + yp_constant_pool_t constant_pool; + + // This is the list of newline offsets in the source file. + yp_newline_list_t newline_list; +}; + +#endif // YARP_PARSER_H diff --git a/src/main/c/yarp/src/yarp/include/yarp/regexp.h b/src/main/c/yarp/include/yarp/regexp.h similarity index 62% rename from src/main/c/yarp/src/yarp/include/yarp/regexp.h rename to src/main/c/yarp/include/yarp/regexp.h index ff98f9a5e1e8..cf624db6b843 100644 --- a/src/main/c/yarp/src/yarp/include/yarp/regexp.h +++ b/src/main/c/yarp/include/yarp/regexp.h @@ -2,19 +2,16 @@ #define YARP_REGEXP_H #include "yarp/defines.h" - #include "yarp/parser.h" +#include "yarp/util/yp_string_list.h" +#include "yarp/util/yp_string.h" + #include #include #include -#include "yarp/util/yp_string_list.h" -#include "yarp/util/yp_string.h" - // Parse a regular expression and extract the names of all of the named capture // groups. -__attribute__((__visibility__("default"))) extern bool -yp_regexp_named_capture_group_names(const char *source, size_t size, - yp_string_list_t *named_captures); +YP_EXPORTED_FUNCTION bool yp_regexp_named_capture_group_names(const char *source, size_t size, yp_string_list_t *named_captures); #endif diff --git a/src/main/c/yarp/include/yarp/unescape.h b/src/main/c/yarp/include/yarp/unescape.h new file mode 100644 index 000000000000..15e7cf2e179a --- /dev/null +++ b/src/main/c/yarp/include/yarp/unescape.h @@ -0,0 +1,38 @@ +#ifndef YARP_UNESCAPE_H +#define YARP_UNESCAPE_H + +#include "yarp/defines.h" +#include "yarp/diagnostic.h" +#include "yarp/parser.h" +#include "yarp/util/yp_char.h" +#include "yarp/util/yp_list.h" +#include "yarp/util/yp_memchr.h" +#include "yarp/util/yp_string.h" + +#include +#include +#include +#include + +// The type of unescape we are performing. +typedef enum { + // When we're creating a string inside of a list literal like %w, we + // shouldn't escape anything. + YP_UNESCAPE_NONE, + + // When we're unescaping a single-quoted string, we only need to unescape + // single quotes and backslashes. + YP_UNESCAPE_MINIMAL, + + // When we're unescaping a double-quoted string, we need to unescape all + // escapes. + YP_UNESCAPE_ALL +} yp_unescape_type_t; + +// Unescape the contents of the given token into the given string using the +// given unescape mode. +YP_EXPORTED_FUNCTION void yp_unescape_manipulate_string(yp_parser_t *parser, const char *value, size_t length, yp_string_t *string, yp_unescape_type_t unescape_type, yp_list_t *error_list); + +YP_EXPORTED_FUNCTION size_t yp_unescape_calculate_difference(const char *value, const char *end, yp_unescape_type_t unescape_type, bool expect_single_codepoint, yp_list_t *error_list); + +#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/util/yp_buffer.h b/src/main/c/yarp/include/yarp/util/yp_buffer.h similarity index 51% rename from src/main/c/yarp/src/yarp/include/yarp/util/yp_buffer.h rename to src/main/c/yarp/include/yarp/util/yp_buffer.h index 4a4eb2f406f4..6a2c0b8f46ee 100644 --- a/src/main/c/yarp/src/yarp/include/yarp/util/yp_buffer.h +++ b/src/main/c/yarp/include/yarp/util/yp_buffer.h @@ -4,6 +4,7 @@ #include "yarp/defines.h" #include +#include #include #include #include @@ -12,33 +13,27 @@ // block of memory. It is used to store the serialized representation of a // YARP tree. typedef struct { - char *value; - size_t length; - size_t capacity; + char *value; + size_t length; + size_t capacity; } yp_buffer_t; -// Allocate a new yp_buffer_t. -__attribute__ ((__visibility__("default"))) extern yp_buffer_t * -yp_buffer_alloc(void); - // Initialize a yp_buffer_t with its default values. -__attribute__ ((__visibility__("default"))) extern void -yp_buffer_init(yp_buffer_t *buffer); +YP_EXPORTED_FUNCTION bool yp_buffer_init(yp_buffer_t *buffer); + +// Append the given amount of space as zeroes to the buffer. +void yp_buffer_append_zeroes(yp_buffer_t *buffer, size_t length); // Append a string to the buffer. -void -yp_buffer_append_str(yp_buffer_t *buffer, const char *value, size_t length); +void yp_buffer_append_str(yp_buffer_t *buffer, const char *value, size_t length); // Append a single byte to the buffer. -void -yp_buffer_append_u8(yp_buffer_t *buffer, uint8_t value); +void yp_buffer_append_u8(yp_buffer_t *buffer, uint8_t value); // Append a 32-bit unsigned integer to the buffer. -void -yp_buffer_append_u32(yp_buffer_t *buffer, uint32_t value); +void yp_buffer_append_u32(yp_buffer_t *buffer, uint32_t value); // Free the memory associated with the buffer. -__attribute__ ((__visibility__("default"))) extern void -yp_buffer_free(yp_buffer_t *buffer); +YP_EXPORTED_FUNCTION void yp_buffer_free(yp_buffer_t *buffer); #endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/util/yp_char.h b/src/main/c/yarp/include/yarp/util/yp_char.h similarity index 61% rename from src/main/c/yarp/src/yarp/include/yarp/util/yp_char.h rename to src/main/c/yarp/include/yarp/util/yp_char.h index 3fc20b84d390..85e5ce4c6561 100644 --- a/src/main/c/yarp/src/yarp/include/yarp/util/yp_char.h +++ b/src/main/c/yarp/include/yarp/util/yp_char.h @@ -2,91 +2,74 @@ #define YP_CHAR_H #include "yarp/defines.h" +#include "yarp/util/yp_newline_list.h" #include #include -bool -yp_char_is_binary_digit(const char c); - -bool -yp_char_is_octal_digit(const char c); - -bool -yp_char_is_hexadecimal_digit(const char c); - // Returns the number of characters at the start of the string that are // whitespace. Disallows searching past the given maximum number of characters. +size_t yp_strspn_whitespace(const char *string, ptrdiff_t length); + +// Returns the number of characters at the start of the string that are +// whitespace while also tracking the location of each newline. Disallows +// searching past the given maximum number of characters. size_t -yp_strspn_whitespace(const char *string, long length); +yp_strspn_whitespace_newlines(const char *string, long length, yp_newline_list_t *newline_list); // Returns the number of characters at the start of the string that are inline // whitespace. Disallows searching past the given maximum number of characters. -size_t -yp_strspn_inline_whitespace(const char *string, long length); +size_t yp_strspn_inline_whitespace(const char *string, ptrdiff_t length); // Returns the number of characters at the start of the string that are decimal // digits. Disallows searching past the given maximum number of characters. -size_t -yp_strspn_decimal_digit(const char *string, long length); +size_t yp_strspn_decimal_digit(const char *string, ptrdiff_t length); // Returns the number of characters at the start of the string that are // hexadecimal digits. Disallows searching past the given maximum number of // characters. -size_t -yp_strspn_hexadecimal_digit(const char *string, long length); +size_t yp_strspn_hexadecimal_digit(const char *string, ptrdiff_t length); // Returns the number of characters at the start of the string that are octal // digits or underscores. Disallows searching past the given maximum number of // characters. -size_t -yp_strspn_octal_number(const char *string, long length); +size_t yp_strspn_octal_number(const char *string, ptrdiff_t length); // Returns the number of characters at the start of the string that are decimal // digits or underscores. Disallows searching past the given maximum number of // characters. -size_t -yp_strspn_decimal_number(const char *string, long length); +size_t yp_strspn_decimal_number(const char *string, ptrdiff_t length); // Returns the number of characters at the start of the string that are // hexadecimal digits or underscores. Disallows searching past the given maximum // number of characters. -size_t -yp_strspn_hexadecimal_number(const char *string, long length); +size_t yp_strspn_hexadecimal_number(const char *string, ptrdiff_t length); // Returns the number of characters at the start of the string that are regexp // options. Disallows searching past the given maximum number of characters. -size_t -yp_strspn_regexp_option(const char *string, long length); +size_t yp_strspn_regexp_option(const char *string, ptrdiff_t length); // Returns the number of characters at the start of the string that are binary // digits or underscores. Disallows searching past the given maximum number of // characters. -size_t -yp_strspn_binary_number(const char *string, long length); +size_t yp_strspn_binary_number(const char *string, ptrdiff_t length); // Returns true if the given character is a whitespace character. -bool -yp_char_is_whitespace(const char c); +bool yp_char_is_whitespace(const char c); // Returns true if the given character is an inline whitespace character. -bool -yp_char_is_inline_whitespace(const char c); +bool yp_char_is_inline_whitespace(const char c); // Returns true if the given character is a binary digit. -bool -yp_char_is_binary_digit(const char c); +bool yp_char_is_binary_digit(const char c); // Returns true if the given character is an octal digit. -bool -yp_char_is_octal_digit(const char c); +bool yp_char_is_octal_digit(const char c); // Returns true if the given character is a decimal digit. -bool -yp_char_is_decimal_digit(const char c); +bool yp_char_is_decimal_digit(const char c); // Returns true if the given character is a hexadecimal digit. -bool -yp_char_is_hexadecimal_digit(const char c); +bool yp_char_is_hexadecimal_digit(const char c); #endif diff --git a/src/main/c/yarp/include/yarp/util/yp_constant_pool.h b/src/main/c/yarp/include/yarp/util/yp_constant_pool.h new file mode 100644 index 000000000000..992f6a57a51b --- /dev/null +++ b/src/main/c/yarp/include/yarp/util/yp_constant_pool.h @@ -0,0 +1,64 @@ +// The constant pool is a data structure that stores a set of strings. Each +// string is assigned a unique id, which can be used to compare strings for +// equality. This comparison ends up being much faster than strcmp, since it +// only requires a single integer comparison. + +#ifndef YP_CONSTANT_POOL_H +#define YP_CONSTANT_POOL_H + +#include "yarp/defines.h" + +#include +#include +#include +#include + +typedef uint32_t yp_constant_id_t; + +typedef struct { + yp_constant_id_t *ids; + size_t size; + size_t capacity; +} yp_constant_id_list_t; + +// Initialize a list of constant ids. +void yp_constant_id_list_init(yp_constant_id_list_t *list); + +// Append a constant id to a list of constant ids. Returns false if any +// potential reallocations fail. +bool yp_constant_id_list_append(yp_constant_id_list_t *list, yp_constant_id_t id); + +// Checks if the current constant id list includes the given constant id. +bool +yp_constant_id_list_includes(yp_constant_id_list_t *list, yp_constant_id_t id); + +// Get the memory size of a list of constant ids. +size_t yp_constant_id_list_memsize(yp_constant_id_list_t *list); + +// Free the memory associated with a list of constant ids. +void yp_constant_id_list_free(yp_constant_id_list_t *list); + +typedef struct { + yp_constant_id_t id; + const char *start; + size_t length; + size_t hash; +} yp_constant_t; + +typedef struct { + yp_constant_t *constants; + size_t size; + size_t capacity; +} yp_constant_pool_t; + +// Initialize a new constant pool with a given capacity. +bool yp_constant_pool_init(yp_constant_pool_t *pool, size_t capacity); + +// Insert a constant into a constant pool. Returns the id of the constant, or 0 +// if any potential calls to resize fail. +yp_constant_id_t yp_constant_pool_insert(yp_constant_pool_t *pool, const char *start, size_t length); + +// Free the memory associated with a constant pool. +void yp_constant_pool_free(yp_constant_pool_t *pool); + +#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/util/yp_list.h b/src/main/c/yarp/include/yarp/util/yp_list.h similarity index 73% rename from src/main/c/yarp/src/yarp/include/yarp/util/yp_list.h rename to src/main/c/yarp/include/yarp/util/yp_list.h index f693a2b7dd63..9c672b34ae19 100644 --- a/src/main/c/yarp/src/yarp/include/yarp/util/yp_list.h +++ b/src/main/c/yarp/include/yarp/util/yp_list.h @@ -15,13 +15,13 @@ // int value; // } yp_int_node_t; // -// yp_list_t *list = yp_list_alloc(); -// yp_list_init(list); +// yp_list_t list; +// yp_list_init(&list); // // yp_int_node_t *node = malloc(sizeof(yp_int_node_t)); // node->value = 5; // -// yp_list_append(list, &node->node); +// yp_list_append(&list, &node->node); // // The yp_list_t struct is used to represent the overall linked list. It // contains a pointer to the head and tail of the list. This allows for easy @@ -39,34 +39,26 @@ // This represents a node in the linked list. typedef struct yp_list_node { - struct yp_list_node *next; + struct yp_list_node *next; } yp_list_node_t; // This represents the overall linked list. It keeps a pointer to the head and // tail so that iteration is easy and pushing new nodes is easy. typedef struct { - yp_list_node_t *head; - yp_list_node_t *tail; + yp_list_node_t *head; + yp_list_node_t *tail; } yp_list_t; -// Allocate a new list. -yp_list_t * -yp_list_alloc(void); - // Initializes a new list. -__attribute__((__visibility__("default"))) extern void -yp_list_init(yp_list_t *list); +YP_EXPORTED_FUNCTION void yp_list_init(yp_list_t *list); // Returns true if the given list is empty. -__attribute__((__visibility__("default"))) extern bool -yp_list_empty_p(yp_list_t *list); +YP_EXPORTED_FUNCTION bool yp_list_empty_p(yp_list_t *list); // Append a node to the given list. -void -yp_list_append(yp_list_t *list, yp_list_node_t *node); +void yp_list_append(yp_list_t *list, yp_list_node_t *node); // Deallocate the internal state of the given list. -__attribute__((__visibility__("default"))) extern void -yp_list_free(yp_list_t *list); +YP_EXPORTED_FUNCTION void yp_list_free(yp_list_t *list); #endif diff --git a/src/main/c/yarp/include/yarp/util/yp_memchr.h b/src/main/c/yarp/include/yarp/util/yp_memchr.h new file mode 100644 index 000000000000..3dae01eebd3a --- /dev/null +++ b/src/main/c/yarp/include/yarp/util/yp_memchr.h @@ -0,0 +1,14 @@ +#ifndef YP_MEMCHR_H +#define YP_MEMCHR_H + +#include "yarp/defines.h" +#include "yarp/parser.h" + +#include + +// We need to roll our own memchr to handle cases where the encoding changes and +// we need to search for a character in a buffer that could be the trailing byte +// of a multibyte character. +void * yp_memchr(yp_parser_t *parser, const void *source, int character, size_t number); + +#endif diff --git a/src/main/c/yarp/include/yarp/util/yp_newline_list.h b/src/main/c/yarp/include/yarp/util/yp_newline_list.h new file mode 100644 index 000000000000..91dbd64a82c5 --- /dev/null +++ b/src/main/c/yarp/include/yarp/util/yp_newline_list.h @@ -0,0 +1,54 @@ +// When compiling the syntax tree, it's necessary to know the line and column +// of many nodes. This is necessary to support things like error messages, +// tracepoints, etc. +// +// It's possible that we could store the start line, start column, end line, and +// end column on every node in addition to the offsets that we already store, +// but that would be quite a lot of memory overhead. + +#ifndef YP_NEWLINE_LIST_H +#define YP_NEWLINE_LIST_H + +#include "yarp/defines.h" + +#include +#include +#include +#include + +// A list of offsets of newlines in a string. The offsets are assumed to be +// sorted/inserted in ascending order. +typedef struct { + const char *start; + + size_t *offsets; + size_t size; + size_t capacity; + + size_t last_offset; + size_t last_index; +} yp_newline_list_t; + +// A line and column in a string. +typedef struct { + size_t line; + size_t column; +} yp_line_column_t; + +// Initialize a new newline list with the given capacity. Returns true if the +// allocation of the offsets succeeds, otherwise returns false. +bool yp_newline_list_init(yp_newline_list_t *list, const char *start, size_t capacity); + +// Append a new offset to the newline list. Returns true if the reallocation of +// the offsets succeeds (if one was necessary), otherwise returns false. +bool yp_newline_list_append(yp_newline_list_t *list, const char *cursor); + +// Returns the line and column of the given offset. If the offset is not in the +// list, the line and column of the closest offset less than the given offset +// are returned. +yp_line_column_t yp_newline_list_line_column(yp_newline_list_t *list, const char *cursor); + +// Free the internal memory allocated for the newline list. +void yp_newline_list_free(yp_newline_list_t *list); + +#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/util/yp_state_stack.h b/src/main/c/yarp/include/yarp/util/yp_state_stack.h similarity index 63% rename from src/main/c/yarp/src/yarp/include/yarp/util/yp_state_stack.h rename to src/main/c/yarp/include/yarp/util/yp_state_stack.h index c163ce79f49c..2f2e2d322e54 100644 --- a/src/main/c/yarp/src/yarp/include/yarp/util/yp_state_stack.h +++ b/src/main/c/yarp/include/yarp/util/yp_state_stack.h @@ -10,19 +10,15 @@ typedef uint32_t yp_state_stack_t; // Initializes the state stack to an empty stack. -void -yp_state_stack_init(yp_state_stack_t *stack); +void yp_state_stack_init(yp_state_stack_t *stack); // Pushes a value onto the stack. -void -yp_state_stack_push(yp_state_stack_t *stack, bool value); +void yp_state_stack_push(yp_state_stack_t *stack, bool value); // Pops a value off the stack. -void -yp_state_stack_pop(yp_state_stack_t *stack); +void yp_state_stack_pop(yp_state_stack_t *stack); // Returns the value at the top of the stack. -bool -yp_state_stack_p(yp_state_stack_t *stack); +bool yp_state_stack_p(yp_state_stack_t *stack); #endif diff --git a/src/main/c/yarp/include/yarp/util/yp_string.h b/src/main/c/yarp/include/yarp/util/yp_string.h new file mode 100644 index 000000000000..eecd71ea5b3c --- /dev/null +++ b/src/main/c/yarp/include/yarp/util/yp_string.h @@ -0,0 +1,57 @@ +#ifndef YARP_STRING_H +#define YARP_STRING_H + +#include "yarp/defines.h" + +#include +#include +#include + +// This struct represents a string value. +typedef struct { + enum { YP_STRING_SHARED, YP_STRING_OWNED, YP_STRING_CONSTANT } type; + + union { + struct { + const char *start; + const char *end; + } shared; + + struct { + char *source; + size_t length; + } owned; + + struct { + const char *source; + size_t length; + } constant; + } as; +} yp_string_t; + +// Initialize a shared string that is based on initial input. +void yp_string_shared_init(yp_string_t *string, const char *start, const char *end); + +// Initialize an owned string that is responsible for freeing allocated memory. +void yp_string_owned_init(yp_string_t *string, char *source, size_t length); + +// Initialize a constant string that doesn't own its memory source. +void yp_string_constant_init(yp_string_t *string, const char *source, size_t length); + +// Returns the memory size associated with the string. +size_t yp_string_memsize(const yp_string_t *string); + +// Ensure the string is owned. If it is not, then reinitialize it as owned and +// copy over the previous source. +void yp_string_ensure_owned(yp_string_t *string); + +// Returns the length associated with the string. +YP_EXPORTED_FUNCTION size_t yp_string_length(const yp_string_t *string); + +// Returns the start pointer associated with the string. +YP_EXPORTED_FUNCTION const char * yp_string_source(const yp_string_t *string); + +// Free the associated memory of the given string. +YP_EXPORTED_FUNCTION void yp_string_free(yp_string_t *string); + +#endif // YARP_STRING_H diff --git a/src/main/c/yarp/include/yarp/util/yp_string_list.h b/src/main/c/yarp/include/yarp/util/yp_string_list.h new file mode 100644 index 000000000000..ae252eb5d5b3 --- /dev/null +++ b/src/main/c/yarp/include/yarp/util/yp_string_list.h @@ -0,0 +1,28 @@ +#ifndef YARP_STRING_LIST_H +#define YARP_STRING_LIST_H + +#include "yarp/defines.h" +#include "yarp/util/yp_string.h" + +#include +#include + +typedef struct { + yp_string_t *strings; + size_t length; + size_t capacity; +} yp_string_list_t; + +// Allocate a new yp_string_list_t. +yp_string_list_t * yp_string_list_alloc(void); + +// Initialize a yp_string_list_t with its default values. +YP_EXPORTED_FUNCTION void yp_string_list_init(yp_string_list_t *string_list); + +// Append a yp_string_t to the given string list. +void yp_string_list_append(yp_string_list_t *string_list, yp_string_t *string); + +// Free the memory associated with the string list. +YP_EXPORTED_FUNCTION void yp_string_list_free(yp_string_list_t *string_list); + +#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/util/yp_strpbrk.h b/src/main/c/yarp/include/yarp/util/yp_strpbrk.h similarity index 65% rename from src/main/c/yarp/src/yarp/include/yarp/util/yp_strpbrk.h rename to src/main/c/yarp/include/yarp/util/yp_strpbrk.h index 13d5b9e7d1ca..7a664d545211 100644 --- a/src/main/c/yarp/src/yarp/include/yarp/util/yp_strpbrk.h +++ b/src/main/c/yarp/include/yarp/util/yp_strpbrk.h @@ -2,6 +2,7 @@ #define YP_STRPBRK_H #include "yarp/defines.h" +#include "yarp/parser.h" #include #include @@ -18,7 +19,11 @@ // also don't want it to stop on null bytes. Ruby actually allows null bytes // within strings, comments, regular expressions, etc. So we need to be able to // skip past them. -const char * -yp_strpbrk(const char *source, const char *charset, long length); +// +// Finally, we want to support encodings wherein the charset could contain +// characters that are trailing bytes of multi-byte characters. For example, in +// Shift-JIS, the backslash character can be a trailing byte. In that case we +// need to take a slower path and iterate one multi-byte character at a time. +const char * yp_strpbrk(yp_parser_t *parser, const char *source, const char *charset, ptrdiff_t length); #endif diff --git a/src/main/c/yarp/src/diagnostic.c b/src/main/c/yarp/src/diagnostic.c new file mode 100644 index 000000000000..8bd888e379bc --- /dev/null +++ b/src/main/c/yarp/src/diagnostic.c @@ -0,0 +1,25 @@ +#include "yarp/diagnostic.h" + +// Append an error to the given list of diagnostic. +bool +yp_diagnostic_list_append(yp_list_t *list, const char *start, const char *end, const char *message) { + yp_diagnostic_t *diagnostic = (yp_diagnostic_t *) malloc(sizeof(yp_diagnostic_t)); + if (diagnostic == NULL) return false; + + *diagnostic = (yp_diagnostic_t) { .start = start, .end = end, .message = message }; + yp_list_append(list, (yp_list_node_t *) diagnostic); + return true; +} + +// Deallocate the internal state of the given diagnostic list. +void +yp_diagnostic_list_free(yp_list_t *list) { + yp_list_node_t *node, *next; + + for (node = list->head; node != NULL; node = next) { + next = node->next; + + yp_diagnostic_t *diagnostic = (yp_diagnostic_t *) node; + free(diagnostic); + } +} diff --git a/src/main/c/yarp/src/enc/ascii.c b/src/main/c/yarp/src/enc/ascii.c new file mode 100644 index 000000000000..655dd49fa65d --- /dev/null +++ b/src/main/c/yarp/src/enc/ascii.c @@ -0,0 +1,65 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ASCII character. +static unsigned char yp_encoding_ascii_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +static size_t +yp_encoding_ascii_char_width(const char *c) { + const unsigned char v = (const unsigned char) *c; + return v < 0x80 ? 1 : 0; +} + +size_t +yp_encoding_ascii_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_ascii_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +size_t +yp_encoding_ascii_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_ascii_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +bool +yp_encoding_ascii_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_ascii_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_ascii = { + .name = "ascii", + .char_width = yp_encoding_ascii_char_width, + .alnum_char = yp_encoding_ascii_alnum_char, + .alpha_char = yp_encoding_ascii_alpha_char, + .isupper_char = yp_encoding_ascii_isupper_char, + .multibyte = false +}; + +yp_encoding_t yp_encoding_ascii_8bit = { + .name = "ascii-8bit", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_ascii_alnum_char, + .alpha_char = yp_encoding_ascii_alpha_char, + .isupper_char = yp_encoding_ascii_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/big5.c b/src/main/c/yarp/src/enc/big5.c new file mode 100644 index 000000000000..23f90c0297f1 --- /dev/null +++ b/src/main/c/yarp/src/enc/big5.c @@ -0,0 +1,79 @@ +#include "yarp/enc/yp_encoding.h" + +typedef uint16_t big5_codepoint_t; + +static big5_codepoint_t +big5_codepoint(const char *c, size_t *width) { + const unsigned char *uc = (const unsigned char *) c; + + // These are the single byte characters. + if (*uc < 0x80) { + *width = 1; + return *uc; + } + + // These are the double byte characters. + if ((uc[0] >= 0xA1 && uc[0] <= 0xFE) && (uc[1] >= 0x40 && uc[1] <= 0xFE)) { + *width = 2; + return (big5_codepoint_t) (uc[0] << 8 | uc[1]); + } + + *width = 0; + return 0; +} + +static size_t +yp_encoding_big5_char_width(const char *c) { + size_t width; + big5_codepoint(c, &width); + + return width; +} + +static size_t +yp_encoding_big5_alpha_char(const char *c) { + size_t width; + big5_codepoint_t codepoint = big5_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_alpha_char(&value); + } else { + return 0; + } +} + +static size_t +yp_encoding_big5_alnum_char(const char *c) { + size_t width; + big5_codepoint_t codepoint = big5_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_alnum_char(&value); + } else { + return 0; + } +} + +static bool +yp_encoding_big5_isupper_char(const char *c) { + size_t width; + big5_codepoint_t codepoint = big5_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_isupper_char(&value); + } else { + return false; + } +} + +yp_encoding_t yp_encoding_big5 = { + .name = "big5", + .char_width = yp_encoding_big5_char_width, + .alnum_char = yp_encoding_big5_alnum_char, + .alpha_char = yp_encoding_big5_alpha_char, + .isupper_char = yp_encoding_big5_isupper_char, + .multibyte = true +}; diff --git a/src/main/c/yarp/src/enc/euc_jp.c b/src/main/c/yarp/src/enc/euc_jp.c new file mode 100644 index 000000000000..3a616e230a9a --- /dev/null +++ b/src/main/c/yarp/src/enc/euc_jp.c @@ -0,0 +1,82 @@ +#include "yarp/enc/yp_encoding.h" + +typedef uint16_t euc_jp_codepoint_t; + +static euc_jp_codepoint_t +euc_jp_codepoint(const char *c, size_t *width) { + const unsigned char *uc = (const unsigned char *) c; + + // These are the single byte characters. + if (*uc < 0x80) { + *width = 1; + return *uc; + } + + // These are the double byte characters. + if ( + ((uc[0] == 0x8E) && (uc[1] >= 0xA1 && uc[1] <= 0xFE)) || + ((uc[0] >= 0xA1 && uc[0] <= 0xFE) && (uc[1] >= 0xA1 && uc[1] <= 0xFE)) + ) { + *width = 2; + return (euc_jp_codepoint_t) (uc[0] << 8 | uc[1]); + } + + *width = 0; + return 0; +} + +static size_t +yp_encoding_euc_jp_char_width(const char *c) { + size_t width; + euc_jp_codepoint(c, &width); + + return width; +} + +static size_t +yp_encoding_euc_jp_alpha_char(const char *c) { + size_t width; + euc_jp_codepoint_t codepoint = euc_jp_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_alpha_char(&value); + } else { + return 0; + } +} + +static size_t +yp_encoding_euc_jp_alnum_char(const char *c) { + size_t width; + euc_jp_codepoint_t codepoint = euc_jp_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_alnum_char(&value); + } else { + return 0; + } +} + +static bool +yp_encoding_euc_jp_isupper_char(const char *c) { + size_t width; + euc_jp_codepoint_t codepoint = euc_jp_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_isupper_char(&value); + } else { + return 0; + } +} + +yp_encoding_t yp_encoding_euc_jp = { + .name = "euc-jp", + .char_width = yp_encoding_euc_jp_char_width, + .alnum_char = yp_encoding_euc_jp_alnum_char, + .alpha_char = yp_encoding_euc_jp_alpha_char, + .isupper_char = yp_encoding_euc_jp_isupper_char, + .multibyte = true +}; diff --git a/src/main/c/yarp/src/enc/gbk.c b/src/main/c/yarp/src/enc/gbk.c new file mode 100644 index 000000000000..3daa1ee3f39d --- /dev/null +++ b/src/main/c/yarp/src/enc/gbk.c @@ -0,0 +1,85 @@ +#include "yarp/enc/yp_encoding.h" + +typedef uint16_t gbk_codepoint_t; + +static gbk_codepoint_t +gbk_codepoint(const char *c, size_t *width) { + const unsigned char *uc = (const unsigned char *) c; + + // These are the single byte characters. + if (*uc < 0x80) { + *width = 1; + return *uc; + } + + // These are the double byte characters. + if ( + ((uc[0] >= 0xA1 && uc[0] <= 0xA9) && (uc[1] >= 0xA1 && uc[1] <= 0xFE)) || // GBK/1 + ((uc[0] >= 0xB0 && uc[0] <= 0xF7) && (uc[1] >= 0xA1 && uc[1] <= 0xFE)) || // GBK/2 + ((uc[0] >= 0x81 && uc[0] <= 0xA0) && (uc[1] >= 0x40 && uc[1] <= 0xFE) && (uc[1] != 0x7F)) || // GBK/3 + ((uc[0] >= 0xAA && uc[0] <= 0xFE) && (uc[1] >= 0x40 && uc[1] <= 0xA0) && (uc[1] != 0x7F)) || // GBK/4 + ((uc[0] >= 0xA8 && uc[0] <= 0xA9) && (uc[1] >= 0x40 && uc[1] <= 0xA0) && (uc[1] != 0x7F)) // GBK/5 + ) { + *width = 2; + return (gbk_codepoint_t) (uc[0] << 8 | uc[1]); + } + + *width = 0; + return 0; +} + +static size_t +yp_encoding_gbk_char_width(const char *c) { + size_t width; + gbk_codepoint(c, &width); + + return width; +} + +static size_t +yp_encoding_gbk_alpha_char(const char *c) { + size_t width; + gbk_codepoint_t codepoint = gbk_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_alpha_char(&value); + } else { + return 0; + } +} + +static size_t +yp_encoding_gbk_alnum_char(const char *c) { + size_t width; + gbk_codepoint_t codepoint = gbk_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_alnum_char(&value); + } else { + return 0; + } +} + +static bool +yp_encoding_gbk_isupper_char(const char *c) { + size_t width; + gbk_codepoint_t codepoint = gbk_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_isupper_char(&value); + } else { + return false; + } +} + +yp_encoding_t yp_encoding_gbk = { + .name = "gbk", + .char_width = yp_encoding_gbk_char_width, + .alnum_char = yp_encoding_gbk_alnum_char, + .alpha_char = yp_encoding_gbk_alpha_char, + .isupper_char = yp_encoding_gbk_isupper_char, + .multibyte = true +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_1.c b/src/main/c/yarp/src/enc/iso_8859_1.c new file mode 100644 index 000000000000..e56a47d223da --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_1.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-1 character. +static unsigned char yp_encoding_iso_8859_1_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +static size_t +yp_encoding_iso_8859_1_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_1_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_1_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_1_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_1_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_1_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_1 = { + .name = "iso-8859-1", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_1_alnum_char, + .alpha_char = yp_encoding_iso_8859_1_alpha_char, + .isupper_char = yp_encoding_iso_8859_1_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_10.c b/src/main/c/yarp/src/enc/iso_8859_10.c new file mode 100644 index 000000000000..d3abf7d09f69 --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_10.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-10 character. +static unsigned char yp_encoding_iso_8859_10_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 0, 7, 7, // Ax + 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +static size_t +yp_encoding_iso_8859_10_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_10_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_10_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_10_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_10_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_10_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_10 = { + .name = "iso-8859-10", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_10_alnum_char, + .alpha_char = yp_encoding_iso_8859_10_alpha_char, + .isupper_char = yp_encoding_iso_8859_10_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_11.c b/src/main/c/yarp/src/enc/iso_8859_11.c new file mode 100644 index 000000000000..45b55fb1efa8 --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_11.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-11 character. +static unsigned char yp_encoding_iso_8859_11_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ax + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Bx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, // Fx +}; + +static size_t +yp_encoding_iso_8859_11_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_11_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_11_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_11_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_11_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_11_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_11 = { + .name = "iso-8859-11", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_11_alnum_char, + .alpha_char = yp_encoding_iso_8859_11_alpha_char, + .isupper_char = yp_encoding_iso_8859_11_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_13.c b/src/main/c/yarp/src/enc/iso_8859_13.c new file mode 100644 index 000000000000..4504779f3661 --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_13.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-13 character. +static unsigned char yp_encoding_iso_8859_13_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +static size_t +yp_encoding_iso_8859_13_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_13_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_13_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_13_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_13_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_13_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_13 = { + .name = "iso-8859-13", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_13_alnum_char, + .alpha_char = yp_encoding_iso_8859_13_alpha_char, + .isupper_char = yp_encoding_iso_8859_13_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_14.c b/src/main/c/yarp/src/enc/iso_8859_14.c new file mode 100644 index 000000000000..7c25610bd088 --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_14.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-14 character. +static unsigned char yp_encoding_iso_8859_14_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 3, 0, 7, 3, 7, 0, 7, 0, 7, 3, 7, 0, 0, 7, // Ax + 7, 3, 7, 3, 7, 3, 0, 7, 3, 3, 3, 7, 3, 7, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +static size_t +yp_encoding_iso_8859_14_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_14_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_14_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_14_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_14_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_14_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_14 = { + .name = "iso-8859-14", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_14_alnum_char, + .alpha_char = yp_encoding_iso_8859_14_alpha_char, + .isupper_char = yp_encoding_iso_8859_14_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_15.c b/src/main/c/yarp/src/enc/iso_8859_15.c new file mode 100644 index 000000000000..e41cc6fbb053 --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_15.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-15 character. +static unsigned char yp_encoding_iso_8859_15_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 7, 0, 3, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 7, 3, 0, 0, 3, 0, 3, 0, 7, 3, 7, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +static size_t +yp_encoding_iso_8859_15_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_15_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_15_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_15_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_15_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_15_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_15 = { + .name = "iso-8859-15", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_15_alnum_char, + .alpha_char = yp_encoding_iso_8859_15_alpha_char, + .isupper_char = yp_encoding_iso_8859_15_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_16.c b/src/main/c/yarp/src/enc/iso_8859_16.c new file mode 100644 index 000000000000..f30067f82a9b --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_16.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-16 character. +static unsigned char yp_encoding_iso_8859_16_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 3, 7, 0, 0, 7, 0, 3, 0, 7, 0, 7, 0, 3, 7, // Ax + 0, 0, 7, 3, 7, 0, 0, 0, 3, 3, 3, 0, 7, 3, 7, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +static size_t +yp_encoding_iso_8859_16_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_16_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_16_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_16_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_16_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_16_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_16 = { + .name = "iso-8859-16", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_16_alnum_char, + .alpha_char = yp_encoding_iso_8859_16_alpha_char, + .isupper_char = yp_encoding_iso_8859_16_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_2.c b/src/main/c/yarp/src/enc/iso_8859_2.c new file mode 100644 index 000000000000..af9536276154 --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_2.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-2 character. +static unsigned char yp_encoding_iso_8859_2_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 0, 7, 0, 7, 7, 0, 0, 7, 7, 7, 7, 0, 7, 7, // Ax + 0, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +static size_t +yp_encoding_iso_8859_2_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_2_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_2_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_2_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_2_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_2_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_2 = { + .name = "iso-8859-2", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_2_alnum_char, + .alpha_char = yp_encoding_iso_8859_2_alpha_char, + .isupper_char = yp_encoding_iso_8859_2_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_3.c b/src/main/c/yarp/src/enc/iso_8859_3.c new file mode 100644 index 000000000000..382a9f239a7c --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_3.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-3 character. +static unsigned char yp_encoding_iso_8859_3_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 0, 0, 0, 0, 7, 0, 0, 7, 7, 7, 7, 0, 0, 7, // Ax + 0, 3, 0, 0, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0, 0, 3, // Bx + 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 0, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +static size_t +yp_encoding_iso_8859_3_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_3_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_3_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_3_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_3_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_3_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_3 = { + .name = "iso-8859-3", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_3_alnum_char, + .alpha_char = yp_encoding_iso_8859_3_alpha_char, + .isupper_char = yp_encoding_iso_8859_3_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_4.c b/src/main/c/yarp/src/enc/iso_8859_4.c new file mode 100644 index 000000000000..5f7d87ecca08 --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_4.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-4 character. +static unsigned char yp_encoding_iso_8859_4_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 3, 7, 0, 7, 7, 0, 0, 7, 7, 7, 7, 0, 7, 0, // Ax + 0, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 7, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +static size_t +yp_encoding_iso_8859_4_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_4_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_4_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_4_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_4_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_4_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_4 = { + .name = "iso-8859-4", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_4_alnum_char, + .alpha_char = yp_encoding_iso_8859_4_alpha_char, + .isupper_char = yp_encoding_iso_8859_4_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_5.c b/src/main/c/yarp/src/enc/iso_8859_5.c new file mode 100644 index 000000000000..ff6d4105d1fc --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_5.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-5 character. +static unsigned char yp_encoding_iso_8859_5_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, // Ax + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, // Fx +}; + +static size_t +yp_encoding_iso_8859_5_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_5_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_5_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_5_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_5_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_5_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_5 = { + .name = "iso-8859-5", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_5_alnum_char, + .alpha_char = yp_encoding_iso_8859_5_alpha_char, + .isupper_char = yp_encoding_iso_8859_5_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_6.c b/src/main/c/yarp/src/enc/iso_8859_6.c new file mode 100644 index 000000000000..b32d3081cdf8 --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_6.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-6 character. +static unsigned char yp_encoding_iso_8859_6_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +static size_t +yp_encoding_iso_8859_6_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_6_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_6_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_6_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_6_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_6_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_6 = { + .name = "iso-8859-6", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_6_alnum_char, + .alpha_char = yp_encoding_iso_8859_6_alpha_char, + .isupper_char = yp_encoding_iso_8859_6_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_7.c b/src/main/c/yarp/src/enc/iso_8859_7.c new file mode 100644 index 000000000000..0ccea26f508c --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_7.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-7 character. +static unsigned char yp_encoding_iso_8859_7_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 7, 0, 7, 7, 7, 0, 7, 0, 7, 7, // Bx + 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, // Fx +}; + +static size_t +yp_encoding_iso_8859_7_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_7_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_7_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_7_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_7_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_7_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_7 = { + .name = "iso-8859-7", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_7_alnum_char, + .alpha_char = yp_encoding_iso_8859_7_alpha_char, + .isupper_char = yp_encoding_iso_8859_7_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_8.c b/src/main/c/yarp/src/enc/iso_8859_8.c new file mode 100644 index 000000000000..718ba8f5b4ed --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_8.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-8 character. +static unsigned char yp_encoding_iso_8859_8_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Fx +}; + +static size_t +yp_encoding_iso_8859_8_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_8_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_8_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_8_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_8_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_8_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_8 = { + .name = "iso-8859-8", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_8_alnum_char, + .alpha_char = yp_encoding_iso_8859_8_alpha_char, + .isupper_char = yp_encoding_iso_8859_8_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/iso_8859_9.c b/src/main/c/yarp/src/enc/iso_8859_9.c new file mode 100644 index 000000000000..3dab740382d6 --- /dev/null +++ b/src/main/c/yarp/src/enc/iso_8859_9.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding ISO-8859-9 character. +static unsigned char yp_encoding_iso_8859_9_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +static size_t +yp_encoding_iso_8859_9_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_9_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_iso_8859_9_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_9_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_iso_8859_9_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_iso_8859_9_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_iso_8859_9 = { + .name = "iso-8859-9", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_iso_8859_9_alnum_char, + .alpha_char = yp_encoding_iso_8859_9_alpha_char, + .isupper_char = yp_encoding_iso_8859_9_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/koi8_r.c b/src/main/c/yarp/src/enc/koi8_r.c new file mode 100644 index 000000000000..cc7788961029 --- /dev/null +++ b/src/main/c/yarp/src/enc/koi8_r.c @@ -0,0 +1,56 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding windows-1251 character. +static unsigned char yp_encoding_koi8_r_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 7, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Dx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Ex + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Fx +}; + +static size_t +yp_encoding_koi8_r_char_width(const char *c) { + const unsigned char v = (const unsigned char) *c; + return ((v >= 0x20 && v <= 0x7E) || (v >= 0x80)) ? 1 : 0; +} + +static size_t +yp_encoding_koi8_r_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_koi8_r_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_koi8_r_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_koi8_r_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_koi8_r_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_koi8_r_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_koi8_r = { + .name = "koi8-r", + .char_width = yp_encoding_koi8_r_char_width, + .alnum_char = yp_encoding_koi8_r_alnum_char, + .alpha_char = yp_encoding_koi8_r_alpha_char, + .isupper_char = yp_encoding_koi8_r_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/yarp/src/enc/shared.c b/src/main/c/yarp/src/enc/shared.c similarity index 72% rename from src/main/c/yarp/src/yarp/src/enc/shared.c rename to src/main/c/yarp/src/enc/shared.c index 359e0934708e..035ab8cc4456 100644 --- a/src/main/c/yarp/src/yarp/src/enc/shared.c +++ b/src/main/c/yarp/src/enc/shared.c @@ -4,6 +4,6 @@ // represent characters. They don't have need of a dynamic function to determine // their width. size_t -yp_encoding_single_char_width(__attribute__((unused)) const char *c) { - return 1; +yp_encoding_single_char_width(YP_ATTRIBUTE_UNUSED const char *c) { + return 1; } diff --git a/src/main/c/yarp/src/enc/shift_jis.c b/src/main/c/yarp/src/enc/shift_jis.c new file mode 100644 index 000000000000..6977d3ce98df --- /dev/null +++ b/src/main/c/yarp/src/enc/shift_jis.c @@ -0,0 +1,82 @@ +#include "yarp/enc/yp_encoding.h" + +typedef uint16_t shift_jis_codepoint_t; + +static shift_jis_codepoint_t +shift_jis_codepoint(const char *c, size_t *width) { + const unsigned char *uc = (const unsigned char *) c; + + // These are the single byte characters. + if (*uc < 0x80 || (*uc >= 0xA1 && *uc <= 0xDF)) { + *width = 1; + return *uc; + } + + // These are the double byte characters. + if ( + ((uc[0] >= 0x81 && uc[0] <= 0x9F) || (uc[0] >= 0xE0 && uc[0] <= 0xFC)) && + (uc[1] >= 0x40 && uc[1] <= 0xFC) + ) { + *width = 2; + return (shift_jis_codepoint_t) (uc[0] << 8 | uc[1]); + } + + *width = 0; + return 0; +} + +static size_t +yp_encoding_shift_jis_char_width(const char *c) { + size_t width; + shift_jis_codepoint(c, &width); + + return width; +} + +static size_t +yp_encoding_shift_jis_alpha_char(const char *c) { + size_t width; + shift_jis_codepoint_t codepoint = shift_jis_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_alpha_char(&value); + } else { + return 0; + } +} + +static size_t +yp_encoding_shift_jis_alnum_char(const char *c) { + size_t width; + shift_jis_codepoint_t codepoint = shift_jis_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_alnum_char(&value); + } else { + return 0; + } +} + +static bool +yp_encoding_shift_jis_isupper_char(const char *c) { + size_t width; + shift_jis_codepoint_t codepoint = shift_jis_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_isupper_char(&value); + } else { + return 0; + } +} + +yp_encoding_t yp_encoding_shift_jis = { + .name = "shift_jis", + .char_width = yp_encoding_shift_jis_char_width, + .alnum_char = yp_encoding_shift_jis_alnum_char, + .alpha_char = yp_encoding_shift_jis_alpha_char, + .isupper_char = yp_encoding_shift_jis_isupper_char, + .multibyte = true +}; diff --git a/src/main/c/yarp/src/enc/unicode.c b/src/main/c/yarp/src/enc/unicode.c new file mode 100644 index 000000000000..63cbf418ddee --- /dev/null +++ b/src/main/c/yarp/src/enc/unicode.c @@ -0,0 +1,2317 @@ +// Note that the UTF-8 decoding code is based on Bjoern Hoehrmann's UTF-8 DFA +// decoder. See http://bjoern.hoehrmann.de/utf-8/decoder/dfa/ for details. + +#include "yarp/enc/yp_encoding.h" + +typedef uint32_t unicode_codepoint_t; + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding unicode codepoint. Note that +// this table is different from other encodings where we used a lookup table +// because the indices of those tables are the byte representations, not the +// codepoints themselves. +unsigned char yp_encoding_unicode_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +#define UNICODE_ALPHA_CODEPOINTS_LENGTH 1450 +static unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEPOINTS_LENGTH] = { + 0x100, 0x2C1, + 0x2C6, 0x2D1, + 0x2E0, 0x2E4, + 0x2EC, 0x2EC, + 0x2EE, 0x2EE, + 0x345, 0x345, + 0x370, 0x374, + 0x376, 0x377, + 0x37A, 0x37D, + 0x37F, 0x37F, + 0x386, 0x386, + 0x388, 0x38A, + 0x38C, 0x38C, + 0x38E, 0x3A1, + 0x3A3, 0x3F5, + 0x3F7, 0x481, + 0x48A, 0x52F, + 0x531, 0x556, + 0x559, 0x559, + 0x560, 0x588, + 0x5B0, 0x5BD, + 0x5BF, 0x5BF, + 0x5C1, 0x5C2, + 0x5C4, 0x5C5, + 0x5C7, 0x5C7, + 0x5D0, 0x5EA, + 0x5EF, 0x5F2, + 0x610, 0x61A, + 0x620, 0x657, + 0x659, 0x65F, + 0x66E, 0x6D3, + 0x6D5, 0x6DC, + 0x6E1, 0x6E8, + 0x6ED, 0x6EF, + 0x6FA, 0x6FC, + 0x6FF, 0x6FF, + 0x710, 0x73F, + 0x74D, 0x7B1, + 0x7CA, 0x7EA, + 0x7F4, 0x7F5, + 0x7FA, 0x7FA, + 0x800, 0x817, + 0x81A, 0x82C, + 0x840, 0x858, + 0x860, 0x86A, + 0x870, 0x887, + 0x889, 0x88E, + 0x8A0, 0x8C9, + 0x8D4, 0x8DF, + 0x8E3, 0x8E9, + 0x8F0, 0x93B, + 0x93D, 0x94C, + 0x94E, 0x950, + 0x955, 0x963, + 0x971, 0x983, + 0x985, 0x98C, + 0x98F, 0x990, + 0x993, 0x9A8, + 0x9AA, 0x9B0, + 0x9B2, 0x9B2, + 0x9B6, 0x9B9, + 0x9BD, 0x9C4, + 0x9C7, 0x9C8, + 0x9CB, 0x9CC, + 0x9CE, 0x9CE, + 0x9D7, 0x9D7, + 0x9DC, 0x9DD, + 0x9DF, 0x9E3, + 0x9F0, 0x9F1, + 0x9FC, 0x9FC, + 0xA01, 0xA03, + 0xA05, 0xA0A, + 0xA0F, 0xA10, + 0xA13, 0xA28, + 0xA2A, 0xA30, + 0xA32, 0xA33, + 0xA35, 0xA36, + 0xA38, 0xA39, + 0xA3E, 0xA42, + 0xA47, 0xA48, + 0xA4B, 0xA4C, + 0xA51, 0xA51, + 0xA59, 0xA5C, + 0xA5E, 0xA5E, + 0xA70, 0xA75, + 0xA81, 0xA83, + 0xA85, 0xA8D, + 0xA8F, 0xA91, + 0xA93, 0xAA8, + 0xAAA, 0xAB0, + 0xAB2, 0xAB3, + 0xAB5, 0xAB9, + 0xABD, 0xAC5, + 0xAC7, 0xAC9, + 0xACB, 0xACC, + 0xAD0, 0xAD0, + 0xAE0, 0xAE3, + 0xAF9, 0xAFC, + 0xB01, 0xB03, + 0xB05, 0xB0C, + 0xB0F, 0xB10, + 0xB13, 0xB28, + 0xB2A, 0xB30, + 0xB32, 0xB33, + 0xB35, 0xB39, + 0xB3D, 0xB44, + 0xB47, 0xB48, + 0xB4B, 0xB4C, + 0xB56, 0xB57, + 0xB5C, 0xB5D, + 0xB5F, 0xB63, + 0xB71, 0xB71, + 0xB82, 0xB83, + 0xB85, 0xB8A, + 0xB8E, 0xB90, + 0xB92, 0xB95, + 0xB99, 0xB9A, + 0xB9C, 0xB9C, + 0xB9E, 0xB9F, + 0xBA3, 0xBA4, + 0xBA8, 0xBAA, + 0xBAE, 0xBB9, + 0xBBE, 0xBC2, + 0xBC6, 0xBC8, + 0xBCA, 0xBCC, + 0xBD0, 0xBD0, + 0xBD7, 0xBD7, + 0xC00, 0xC0C, + 0xC0E, 0xC10, + 0xC12, 0xC28, + 0xC2A, 0xC39, + 0xC3D, 0xC44, + 0xC46, 0xC48, + 0xC4A, 0xC4C, + 0xC55, 0xC56, + 0xC58, 0xC5A, + 0xC5D, 0xC5D, + 0xC60, 0xC63, + 0xC80, 0xC83, + 0xC85, 0xC8C, + 0xC8E, 0xC90, + 0xC92, 0xCA8, + 0xCAA, 0xCB3, + 0xCB5, 0xCB9, + 0xCBD, 0xCC4, + 0xCC6, 0xCC8, + 0xCCA, 0xCCC, + 0xCD5, 0xCD6, + 0xCDD, 0xCDE, + 0xCE0, 0xCE3, + 0xCF1, 0xCF3, + 0xD00, 0xD0C, + 0xD0E, 0xD10, + 0xD12, 0xD3A, + 0xD3D, 0xD44, + 0xD46, 0xD48, + 0xD4A, 0xD4C, + 0xD4E, 0xD4E, + 0xD54, 0xD57, + 0xD5F, 0xD63, + 0xD7A, 0xD7F, + 0xD81, 0xD83, + 0xD85, 0xD96, + 0xD9A, 0xDB1, + 0xDB3, 0xDBB, + 0xDBD, 0xDBD, + 0xDC0, 0xDC6, + 0xDCF, 0xDD4, + 0xDD6, 0xDD6, + 0xDD8, 0xDDF, + 0xDF2, 0xDF3, + 0xE01, 0xE3A, + 0xE40, 0xE46, + 0xE4D, 0xE4D, + 0xE81, 0xE82, + 0xE84, 0xE84, + 0xE86, 0xE8A, + 0xE8C, 0xEA3, + 0xEA5, 0xEA5, + 0xEA7, 0xEB9, + 0xEBB, 0xEBD, + 0xEC0, 0xEC4, + 0xEC6, 0xEC6, + 0xECD, 0xECD, + 0xEDC, 0xEDF, + 0xF00, 0xF00, + 0xF40, 0xF47, + 0xF49, 0xF6C, + 0xF71, 0xF83, + 0xF88, 0xF97, + 0xF99, 0xFBC, + 0x1000, 0x1036, + 0x1038, 0x1038, + 0x103B, 0x103F, + 0x1050, 0x108F, + 0x109A, 0x109D, + 0x10A0, 0x10C5, + 0x10C7, 0x10C7, + 0x10CD, 0x10CD, + 0x10D0, 0x10FA, + 0x10FC, 0x1248, + 0x124A, 0x124D, + 0x1250, 0x1256, + 0x1258, 0x1258, + 0x125A, 0x125D, + 0x1260, 0x1288, + 0x128A, 0x128D, + 0x1290, 0x12B0, + 0x12B2, 0x12B5, + 0x12B8, 0x12BE, + 0x12C0, 0x12C0, + 0x12C2, 0x12C5, + 0x12C8, 0x12D6, + 0x12D8, 0x1310, + 0x1312, 0x1315, + 0x1318, 0x135A, + 0x1380, 0x138F, + 0x13A0, 0x13F5, + 0x13F8, 0x13FD, + 0x1401, 0x166C, + 0x166F, 0x167F, + 0x1681, 0x169A, + 0x16A0, 0x16EA, + 0x16EE, 0x16F8, + 0x1700, 0x1713, + 0x171F, 0x1733, + 0x1740, 0x1753, + 0x1760, 0x176C, + 0x176E, 0x1770, + 0x1772, 0x1773, + 0x1780, 0x17B3, + 0x17B6, 0x17C8, + 0x17D7, 0x17D7, + 0x17DC, 0x17DC, + 0x1820, 0x1878, + 0x1880, 0x18AA, + 0x18B0, 0x18F5, + 0x1900, 0x191E, + 0x1920, 0x192B, + 0x1930, 0x1938, + 0x1950, 0x196D, + 0x1970, 0x1974, + 0x1980, 0x19AB, + 0x19B0, 0x19C9, + 0x1A00, 0x1A1B, + 0x1A20, 0x1A5E, + 0x1A61, 0x1A74, + 0x1AA7, 0x1AA7, + 0x1ABF, 0x1AC0, + 0x1ACC, 0x1ACE, + 0x1B00, 0x1B33, + 0x1B35, 0x1B43, + 0x1B45, 0x1B4C, + 0x1B80, 0x1BA9, + 0x1BAC, 0x1BAF, + 0x1BBA, 0x1BE5, + 0x1BE7, 0x1BF1, + 0x1C00, 0x1C36, + 0x1C4D, 0x1C4F, + 0x1C5A, 0x1C7D, + 0x1C80, 0x1C88, + 0x1C90, 0x1CBA, + 0x1CBD, 0x1CBF, + 0x1CE9, 0x1CEC, + 0x1CEE, 0x1CF3, + 0x1CF5, 0x1CF6, + 0x1CFA, 0x1CFA, + 0x1D00, 0x1DBF, + 0x1DE7, 0x1DF4, + 0x1E00, 0x1F15, + 0x1F18, 0x1F1D, + 0x1F20, 0x1F45, + 0x1F48, 0x1F4D, + 0x1F50, 0x1F57, + 0x1F59, 0x1F59, + 0x1F5B, 0x1F5B, + 0x1F5D, 0x1F5D, + 0x1F5F, 0x1F7D, + 0x1F80, 0x1FB4, + 0x1FB6, 0x1FBC, + 0x1FBE, 0x1FBE, + 0x1FC2, 0x1FC4, + 0x1FC6, 0x1FCC, + 0x1FD0, 0x1FD3, + 0x1FD6, 0x1FDB, + 0x1FE0, 0x1FEC, + 0x1FF2, 0x1FF4, + 0x1FF6, 0x1FFC, + 0x2071, 0x2071, + 0x207F, 0x207F, + 0x2090, 0x209C, + 0x2102, 0x2102, + 0x2107, 0x2107, + 0x210A, 0x2113, + 0x2115, 0x2115, + 0x2119, 0x211D, + 0x2124, 0x2124, + 0x2126, 0x2126, + 0x2128, 0x2128, + 0x212A, 0x212D, + 0x212F, 0x2139, + 0x213C, 0x213F, + 0x2145, 0x2149, + 0x214E, 0x214E, + 0x2160, 0x2188, + 0x24B6, 0x24E9, + 0x2C00, 0x2CE4, + 0x2CEB, 0x2CEE, + 0x2CF2, 0x2CF3, + 0x2D00, 0x2D25, + 0x2D27, 0x2D27, + 0x2D2D, 0x2D2D, + 0x2D30, 0x2D67, + 0x2D6F, 0x2D6F, + 0x2D80, 0x2D96, + 0x2DA0, 0x2DA6, + 0x2DA8, 0x2DAE, + 0x2DB0, 0x2DB6, + 0x2DB8, 0x2DBE, + 0x2DC0, 0x2DC6, + 0x2DC8, 0x2DCE, + 0x2DD0, 0x2DD6, + 0x2DD8, 0x2DDE, + 0x2DE0, 0x2DFF, + 0x2E2F, 0x2E2F, + 0x3005, 0x3007, + 0x3021, 0x3029, + 0x3031, 0x3035, + 0x3038, 0x303C, + 0x3041, 0x3096, + 0x309D, 0x309F, + 0x30A1, 0x30FA, + 0x30FC, 0x30FF, + 0x3105, 0x312F, + 0x3131, 0x318E, + 0x31A0, 0x31BF, + 0x31F0, 0x31FF, + 0x3400, 0x4DBF, + 0x4E00, 0xA48C, + 0xA4D0, 0xA4FD, + 0xA500, 0xA60C, + 0xA610, 0xA61F, + 0xA62A, 0xA62B, + 0xA640, 0xA66E, + 0xA674, 0xA67B, + 0xA67F, 0xA6EF, + 0xA717, 0xA71F, + 0xA722, 0xA788, + 0xA78B, 0xA7CA, + 0xA7D0, 0xA7D1, + 0xA7D3, 0xA7D3, + 0xA7D5, 0xA7D9, + 0xA7F2, 0xA805, + 0xA807, 0xA827, + 0xA840, 0xA873, + 0xA880, 0xA8C3, + 0xA8C5, 0xA8C5, + 0xA8F2, 0xA8F7, + 0xA8FB, 0xA8FB, + 0xA8FD, 0xA8FF, + 0xA90A, 0xA92A, + 0xA930, 0xA952, + 0xA960, 0xA97C, + 0xA980, 0xA9B2, + 0xA9B4, 0xA9BF, + 0xA9CF, 0xA9CF, + 0xA9E0, 0xA9EF, + 0xA9FA, 0xA9FE, + 0xAA00, 0xAA36, + 0xAA40, 0xAA4D, + 0xAA60, 0xAA76, + 0xAA7A, 0xAABE, + 0xAAC0, 0xAAC0, + 0xAAC2, 0xAAC2, + 0xAADB, 0xAADD, + 0xAAE0, 0xAAEF, + 0xAAF2, 0xAAF5, + 0xAB01, 0xAB06, + 0xAB09, 0xAB0E, + 0xAB11, 0xAB16, + 0xAB20, 0xAB26, + 0xAB28, 0xAB2E, + 0xAB30, 0xAB5A, + 0xAB5C, 0xAB69, + 0xAB70, 0xABEA, + 0xAC00, 0xD7A3, + 0xD7B0, 0xD7C6, + 0xD7CB, 0xD7FB, + 0xF900, 0xFA6D, + 0xFA70, 0xFAD9, + 0xFB00, 0xFB06, + 0xFB13, 0xFB17, + 0xFB1D, 0xFB28, + 0xFB2A, 0xFB36, + 0xFB38, 0xFB3C, + 0xFB3E, 0xFB3E, + 0xFB40, 0xFB41, + 0xFB43, 0xFB44, + 0xFB46, 0xFBB1, + 0xFBD3, 0xFD3D, + 0xFD50, 0xFD8F, + 0xFD92, 0xFDC7, + 0xFDF0, 0xFDFB, + 0xFE70, 0xFE74, + 0xFE76, 0xFEFC, + 0xFF21, 0xFF3A, + 0xFF41, 0xFF5A, + 0xFF66, 0xFFBE, + 0xFFC2, 0xFFC7, + 0xFFCA, 0xFFCF, + 0xFFD2, 0xFFD7, + 0xFFDA, 0xFFDC, + 0x10000, 0x1000B, + 0x1000D, 0x10026, + 0x10028, 0x1003A, + 0x1003C, 0x1003D, + 0x1003F, 0x1004D, + 0x10050, 0x1005D, + 0x10080, 0x100FA, + 0x10140, 0x10174, + 0x10280, 0x1029C, + 0x102A0, 0x102D0, + 0x10300, 0x1031F, + 0x1032D, 0x1034A, + 0x10350, 0x1037A, + 0x10380, 0x1039D, + 0x103A0, 0x103C3, + 0x103C8, 0x103CF, + 0x103D1, 0x103D5, + 0x10400, 0x1049D, + 0x104B0, 0x104D3, + 0x104D8, 0x104FB, + 0x10500, 0x10527, + 0x10530, 0x10563, + 0x10570, 0x1057A, + 0x1057C, 0x1058A, + 0x1058C, 0x10592, + 0x10594, 0x10595, + 0x10597, 0x105A1, + 0x105A3, 0x105B1, + 0x105B3, 0x105B9, + 0x105BB, 0x105BC, + 0x10600, 0x10736, + 0x10740, 0x10755, + 0x10760, 0x10767, + 0x10780, 0x10785, + 0x10787, 0x107B0, + 0x107B2, 0x107BA, + 0x10800, 0x10805, + 0x10808, 0x10808, + 0x1080A, 0x10835, + 0x10837, 0x10838, + 0x1083C, 0x1083C, + 0x1083F, 0x10855, + 0x10860, 0x10876, + 0x10880, 0x1089E, + 0x108E0, 0x108F2, + 0x108F4, 0x108F5, + 0x10900, 0x10915, + 0x10920, 0x10939, + 0x10980, 0x109B7, + 0x109BE, 0x109BF, + 0x10A00, 0x10A03, + 0x10A05, 0x10A06, + 0x10A0C, 0x10A13, + 0x10A15, 0x10A17, + 0x10A19, 0x10A35, + 0x10A60, 0x10A7C, + 0x10A80, 0x10A9C, + 0x10AC0, 0x10AC7, + 0x10AC9, 0x10AE4, + 0x10B00, 0x10B35, + 0x10B40, 0x10B55, + 0x10B60, 0x10B72, + 0x10B80, 0x10B91, + 0x10C00, 0x10C48, + 0x10C80, 0x10CB2, + 0x10CC0, 0x10CF2, + 0x10D00, 0x10D27, + 0x10E80, 0x10EA9, + 0x10EAB, 0x10EAC, + 0x10EB0, 0x10EB1, + 0x10F00, 0x10F1C, + 0x10F27, 0x10F27, + 0x10F30, 0x10F45, + 0x10F70, 0x10F81, + 0x10FB0, 0x10FC4, + 0x10FE0, 0x10FF6, + 0x11000, 0x11045, + 0x11071, 0x11075, + 0x11080, 0x110B8, + 0x110C2, 0x110C2, + 0x110D0, 0x110E8, + 0x11100, 0x11132, + 0x11144, 0x11147, + 0x11150, 0x11172, + 0x11176, 0x11176, + 0x11180, 0x111BF, + 0x111C1, 0x111C4, + 0x111CE, 0x111CF, + 0x111DA, 0x111DA, + 0x111DC, 0x111DC, + 0x11200, 0x11211, + 0x11213, 0x11234, + 0x11237, 0x11237, + 0x1123E, 0x11241, + 0x11280, 0x11286, + 0x11288, 0x11288, + 0x1128A, 0x1128D, + 0x1128F, 0x1129D, + 0x1129F, 0x112A8, + 0x112B0, 0x112E8, + 0x11300, 0x11303, + 0x11305, 0x1130C, + 0x1130F, 0x11310, + 0x11313, 0x11328, + 0x1132A, 0x11330, + 0x11332, 0x11333, + 0x11335, 0x11339, + 0x1133D, 0x11344, + 0x11347, 0x11348, + 0x1134B, 0x1134C, + 0x11350, 0x11350, + 0x11357, 0x11357, + 0x1135D, 0x11363, + 0x11400, 0x11441, + 0x11443, 0x11445, + 0x11447, 0x1144A, + 0x1145F, 0x11461, + 0x11480, 0x114C1, + 0x114C4, 0x114C5, + 0x114C7, 0x114C7, + 0x11580, 0x115B5, + 0x115B8, 0x115BE, + 0x115D8, 0x115DD, + 0x11600, 0x1163E, + 0x11640, 0x11640, + 0x11644, 0x11644, + 0x11680, 0x116B5, + 0x116B8, 0x116B8, + 0x11700, 0x1171A, + 0x1171D, 0x1172A, + 0x11740, 0x11746, + 0x11800, 0x11838, + 0x118A0, 0x118DF, + 0x118FF, 0x11906, + 0x11909, 0x11909, + 0x1190C, 0x11913, + 0x11915, 0x11916, + 0x11918, 0x11935, + 0x11937, 0x11938, + 0x1193B, 0x1193C, + 0x1193F, 0x11942, + 0x119A0, 0x119A7, + 0x119AA, 0x119D7, + 0x119DA, 0x119DF, + 0x119E1, 0x119E1, + 0x119E3, 0x119E4, + 0x11A00, 0x11A32, + 0x11A35, 0x11A3E, + 0x11A50, 0x11A97, + 0x11A9D, 0x11A9D, + 0x11AB0, 0x11AF8, + 0x11C00, 0x11C08, + 0x11C0A, 0x11C36, + 0x11C38, 0x11C3E, + 0x11C40, 0x11C40, + 0x11C72, 0x11C8F, + 0x11C92, 0x11CA7, + 0x11CA9, 0x11CB6, + 0x11D00, 0x11D06, + 0x11D08, 0x11D09, + 0x11D0B, 0x11D36, + 0x11D3A, 0x11D3A, + 0x11D3C, 0x11D3D, + 0x11D3F, 0x11D41, + 0x11D43, 0x11D43, + 0x11D46, 0x11D47, + 0x11D60, 0x11D65, + 0x11D67, 0x11D68, + 0x11D6A, 0x11D8E, + 0x11D90, 0x11D91, + 0x11D93, 0x11D96, + 0x11D98, 0x11D98, + 0x11EE0, 0x11EF6, + 0x11F00, 0x11F10, + 0x11F12, 0x11F3A, + 0x11F3E, 0x11F40, + 0x11FB0, 0x11FB0, + 0x12000, 0x12399, + 0x12400, 0x1246E, + 0x12480, 0x12543, + 0x12F90, 0x12FF0, + 0x13000, 0x1342F, + 0x13441, 0x13446, + 0x14400, 0x14646, + 0x16800, 0x16A38, + 0x16A40, 0x16A5E, + 0x16A70, 0x16ABE, + 0x16AD0, 0x16AED, + 0x16B00, 0x16B2F, + 0x16B40, 0x16B43, + 0x16B63, 0x16B77, + 0x16B7D, 0x16B8F, + 0x16E40, 0x16E7F, + 0x16F00, 0x16F4A, + 0x16F4F, 0x16F87, + 0x16F8F, 0x16F9F, + 0x16FE0, 0x16FE1, + 0x16FE3, 0x16FE3, + 0x16FF0, 0x16FF1, + 0x17000, 0x187F7, + 0x18800, 0x18CD5, + 0x18D00, 0x18D08, + 0x1AFF0, 0x1AFF3, + 0x1AFF5, 0x1AFFB, + 0x1AFFD, 0x1AFFE, + 0x1B000, 0x1B122, + 0x1B132, 0x1B132, + 0x1B150, 0x1B152, + 0x1B155, 0x1B155, + 0x1B164, 0x1B167, + 0x1B170, 0x1B2FB, + 0x1BC00, 0x1BC6A, + 0x1BC70, 0x1BC7C, + 0x1BC80, 0x1BC88, + 0x1BC90, 0x1BC99, + 0x1BC9E, 0x1BC9E, + 0x1D400, 0x1D454, + 0x1D456, 0x1D49C, + 0x1D49E, 0x1D49F, + 0x1D4A2, 0x1D4A2, + 0x1D4A5, 0x1D4A6, + 0x1D4A9, 0x1D4AC, + 0x1D4AE, 0x1D4B9, + 0x1D4BB, 0x1D4BB, + 0x1D4BD, 0x1D4C3, + 0x1D4C5, 0x1D505, + 0x1D507, 0x1D50A, + 0x1D50D, 0x1D514, + 0x1D516, 0x1D51C, + 0x1D51E, 0x1D539, + 0x1D53B, 0x1D53E, + 0x1D540, 0x1D544, + 0x1D546, 0x1D546, + 0x1D54A, 0x1D550, + 0x1D552, 0x1D6A5, + 0x1D6A8, 0x1D6C0, + 0x1D6C2, 0x1D6DA, + 0x1D6DC, 0x1D6FA, + 0x1D6FC, 0x1D714, + 0x1D716, 0x1D734, + 0x1D736, 0x1D74E, + 0x1D750, 0x1D76E, + 0x1D770, 0x1D788, + 0x1D78A, 0x1D7A8, + 0x1D7AA, 0x1D7C2, + 0x1D7C4, 0x1D7CB, + 0x1DF00, 0x1DF1E, + 0x1DF25, 0x1DF2A, + 0x1E000, 0x1E006, + 0x1E008, 0x1E018, + 0x1E01B, 0x1E021, + 0x1E023, 0x1E024, + 0x1E026, 0x1E02A, + 0x1E030, 0x1E06D, + 0x1E08F, 0x1E08F, + 0x1E100, 0x1E12C, + 0x1E137, 0x1E13D, + 0x1E14E, 0x1E14E, + 0x1E290, 0x1E2AD, + 0x1E2C0, 0x1E2EB, + 0x1E4D0, 0x1E4EB, + 0x1E7E0, 0x1E7E6, + 0x1E7E8, 0x1E7EB, + 0x1E7ED, 0x1E7EE, + 0x1E7F0, 0x1E7FE, + 0x1E800, 0x1E8C4, + 0x1E900, 0x1E943, + 0x1E947, 0x1E947, + 0x1E94B, 0x1E94B, + 0x1EE00, 0x1EE03, + 0x1EE05, 0x1EE1F, + 0x1EE21, 0x1EE22, + 0x1EE24, 0x1EE24, + 0x1EE27, 0x1EE27, + 0x1EE29, 0x1EE32, + 0x1EE34, 0x1EE37, + 0x1EE39, 0x1EE39, + 0x1EE3B, 0x1EE3B, + 0x1EE42, 0x1EE42, + 0x1EE47, 0x1EE47, + 0x1EE49, 0x1EE49, + 0x1EE4B, 0x1EE4B, + 0x1EE4D, 0x1EE4F, + 0x1EE51, 0x1EE52, + 0x1EE54, 0x1EE54, + 0x1EE57, 0x1EE57, + 0x1EE59, 0x1EE59, + 0x1EE5B, 0x1EE5B, + 0x1EE5D, 0x1EE5D, + 0x1EE5F, 0x1EE5F, + 0x1EE61, 0x1EE62, + 0x1EE64, 0x1EE64, + 0x1EE67, 0x1EE6A, + 0x1EE6C, 0x1EE72, + 0x1EE74, 0x1EE77, + 0x1EE79, 0x1EE7C, + 0x1EE7E, 0x1EE7E, + 0x1EE80, 0x1EE89, + 0x1EE8B, 0x1EE9B, + 0x1EEA1, 0x1EEA3, + 0x1EEA5, 0x1EEA9, + 0x1EEAB, 0x1EEBB, + 0x1F130, 0x1F149, + 0x1F150, 0x1F169, + 0x1F170, 0x1F189, + 0x20000, 0x2A6DF, + 0x2A700, 0x2B739, + 0x2B740, 0x2B81D, + 0x2B820, 0x2CEA1, + 0x2CEB0, 0x2EBE0, + 0x2F800, 0x2FA1D, + 0x30000, 0x3134A, + 0x31350, 0x323AF, +}; + +#define UNICODE_ALNUM_CODEPOINTS_LENGTH 1528 +static unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEPOINTS_LENGTH] = { + 0x100, 0x2C1, + 0x2C6, 0x2D1, + 0x2E0, 0x2E4, + 0x2EC, 0x2EC, + 0x2EE, 0x2EE, + 0x345, 0x345, + 0x370, 0x374, + 0x376, 0x377, + 0x37A, 0x37D, + 0x37F, 0x37F, + 0x386, 0x386, + 0x388, 0x38A, + 0x38C, 0x38C, + 0x38E, 0x3A1, + 0x3A3, 0x3F5, + 0x3F7, 0x481, + 0x48A, 0x52F, + 0x531, 0x556, + 0x559, 0x559, + 0x560, 0x588, + 0x5B0, 0x5BD, + 0x5BF, 0x5BF, + 0x5C1, 0x5C2, + 0x5C4, 0x5C5, + 0x5C7, 0x5C7, + 0x5D0, 0x5EA, + 0x5EF, 0x5F2, + 0x610, 0x61A, + 0x620, 0x657, + 0x659, 0x669, + 0x66E, 0x6D3, + 0x6D5, 0x6DC, + 0x6E1, 0x6E8, + 0x6ED, 0x6FC, + 0x6FF, 0x6FF, + 0x710, 0x73F, + 0x74D, 0x7B1, + 0x7C0, 0x7EA, + 0x7F4, 0x7F5, + 0x7FA, 0x7FA, + 0x800, 0x817, + 0x81A, 0x82C, + 0x840, 0x858, + 0x860, 0x86A, + 0x870, 0x887, + 0x889, 0x88E, + 0x8A0, 0x8C9, + 0x8D4, 0x8DF, + 0x8E3, 0x8E9, + 0x8F0, 0x93B, + 0x93D, 0x94C, + 0x94E, 0x950, + 0x955, 0x963, + 0x966, 0x96F, + 0x971, 0x983, + 0x985, 0x98C, + 0x98F, 0x990, + 0x993, 0x9A8, + 0x9AA, 0x9B0, + 0x9B2, 0x9B2, + 0x9B6, 0x9B9, + 0x9BD, 0x9C4, + 0x9C7, 0x9C8, + 0x9CB, 0x9CC, + 0x9CE, 0x9CE, + 0x9D7, 0x9D7, + 0x9DC, 0x9DD, + 0x9DF, 0x9E3, + 0x9E6, 0x9F1, + 0x9FC, 0x9FC, + 0xA01, 0xA03, + 0xA05, 0xA0A, + 0xA0F, 0xA10, + 0xA13, 0xA28, + 0xA2A, 0xA30, + 0xA32, 0xA33, + 0xA35, 0xA36, + 0xA38, 0xA39, + 0xA3E, 0xA42, + 0xA47, 0xA48, + 0xA4B, 0xA4C, + 0xA51, 0xA51, + 0xA59, 0xA5C, + 0xA5E, 0xA5E, + 0xA66, 0xA75, + 0xA81, 0xA83, + 0xA85, 0xA8D, + 0xA8F, 0xA91, + 0xA93, 0xAA8, + 0xAAA, 0xAB0, + 0xAB2, 0xAB3, + 0xAB5, 0xAB9, + 0xABD, 0xAC5, + 0xAC7, 0xAC9, + 0xACB, 0xACC, + 0xAD0, 0xAD0, + 0xAE0, 0xAE3, + 0xAE6, 0xAEF, + 0xAF9, 0xAFC, + 0xB01, 0xB03, + 0xB05, 0xB0C, + 0xB0F, 0xB10, + 0xB13, 0xB28, + 0xB2A, 0xB30, + 0xB32, 0xB33, + 0xB35, 0xB39, + 0xB3D, 0xB44, + 0xB47, 0xB48, + 0xB4B, 0xB4C, + 0xB56, 0xB57, + 0xB5C, 0xB5D, + 0xB5F, 0xB63, + 0xB66, 0xB6F, + 0xB71, 0xB71, + 0xB82, 0xB83, + 0xB85, 0xB8A, + 0xB8E, 0xB90, + 0xB92, 0xB95, + 0xB99, 0xB9A, + 0xB9C, 0xB9C, + 0xB9E, 0xB9F, + 0xBA3, 0xBA4, + 0xBA8, 0xBAA, + 0xBAE, 0xBB9, + 0xBBE, 0xBC2, + 0xBC6, 0xBC8, + 0xBCA, 0xBCC, + 0xBD0, 0xBD0, + 0xBD7, 0xBD7, + 0xBE6, 0xBEF, + 0xC00, 0xC0C, + 0xC0E, 0xC10, + 0xC12, 0xC28, + 0xC2A, 0xC39, + 0xC3D, 0xC44, + 0xC46, 0xC48, + 0xC4A, 0xC4C, + 0xC55, 0xC56, + 0xC58, 0xC5A, + 0xC5D, 0xC5D, + 0xC60, 0xC63, + 0xC66, 0xC6F, + 0xC80, 0xC83, + 0xC85, 0xC8C, + 0xC8E, 0xC90, + 0xC92, 0xCA8, + 0xCAA, 0xCB3, + 0xCB5, 0xCB9, + 0xCBD, 0xCC4, + 0xCC6, 0xCC8, + 0xCCA, 0xCCC, + 0xCD5, 0xCD6, + 0xCDD, 0xCDE, + 0xCE0, 0xCE3, + 0xCE6, 0xCEF, + 0xCF1, 0xCF3, + 0xD00, 0xD0C, + 0xD0E, 0xD10, + 0xD12, 0xD3A, + 0xD3D, 0xD44, + 0xD46, 0xD48, + 0xD4A, 0xD4C, + 0xD4E, 0xD4E, + 0xD54, 0xD57, + 0xD5F, 0xD63, + 0xD66, 0xD6F, + 0xD7A, 0xD7F, + 0xD81, 0xD83, + 0xD85, 0xD96, + 0xD9A, 0xDB1, + 0xDB3, 0xDBB, + 0xDBD, 0xDBD, + 0xDC0, 0xDC6, + 0xDCF, 0xDD4, + 0xDD6, 0xDD6, + 0xDD8, 0xDDF, + 0xDE6, 0xDEF, + 0xDF2, 0xDF3, + 0xE01, 0xE3A, + 0xE40, 0xE46, + 0xE4D, 0xE4D, + 0xE50, 0xE59, + 0xE81, 0xE82, + 0xE84, 0xE84, + 0xE86, 0xE8A, + 0xE8C, 0xEA3, + 0xEA5, 0xEA5, + 0xEA7, 0xEB9, + 0xEBB, 0xEBD, + 0xEC0, 0xEC4, + 0xEC6, 0xEC6, + 0xECD, 0xECD, + 0xED0, 0xED9, + 0xEDC, 0xEDF, + 0xF00, 0xF00, + 0xF20, 0xF29, + 0xF40, 0xF47, + 0xF49, 0xF6C, + 0xF71, 0xF83, + 0xF88, 0xF97, + 0xF99, 0xFBC, + 0x1000, 0x1036, + 0x1038, 0x1038, + 0x103B, 0x1049, + 0x1050, 0x109D, + 0x10A0, 0x10C5, + 0x10C7, 0x10C7, + 0x10CD, 0x10CD, + 0x10D0, 0x10FA, + 0x10FC, 0x1248, + 0x124A, 0x124D, + 0x1250, 0x1256, + 0x1258, 0x1258, + 0x125A, 0x125D, + 0x1260, 0x1288, + 0x128A, 0x128D, + 0x1290, 0x12B0, + 0x12B2, 0x12B5, + 0x12B8, 0x12BE, + 0x12C0, 0x12C0, + 0x12C2, 0x12C5, + 0x12C8, 0x12D6, + 0x12D8, 0x1310, + 0x1312, 0x1315, + 0x1318, 0x135A, + 0x1380, 0x138F, + 0x13A0, 0x13F5, + 0x13F8, 0x13FD, + 0x1401, 0x166C, + 0x166F, 0x167F, + 0x1681, 0x169A, + 0x16A0, 0x16EA, + 0x16EE, 0x16F8, + 0x1700, 0x1713, + 0x171F, 0x1733, + 0x1740, 0x1753, + 0x1760, 0x176C, + 0x176E, 0x1770, + 0x1772, 0x1773, + 0x1780, 0x17B3, + 0x17B6, 0x17C8, + 0x17D7, 0x17D7, + 0x17DC, 0x17DC, + 0x17E0, 0x17E9, + 0x1810, 0x1819, + 0x1820, 0x1878, + 0x1880, 0x18AA, + 0x18B0, 0x18F5, + 0x1900, 0x191E, + 0x1920, 0x192B, + 0x1930, 0x1938, + 0x1946, 0x196D, + 0x1970, 0x1974, + 0x1980, 0x19AB, + 0x19B0, 0x19C9, + 0x19D0, 0x19D9, + 0x1A00, 0x1A1B, + 0x1A20, 0x1A5E, + 0x1A61, 0x1A74, + 0x1A80, 0x1A89, + 0x1A90, 0x1A99, + 0x1AA7, 0x1AA7, + 0x1ABF, 0x1AC0, + 0x1ACC, 0x1ACE, + 0x1B00, 0x1B33, + 0x1B35, 0x1B43, + 0x1B45, 0x1B4C, + 0x1B50, 0x1B59, + 0x1B80, 0x1BA9, + 0x1BAC, 0x1BE5, + 0x1BE7, 0x1BF1, + 0x1C00, 0x1C36, + 0x1C40, 0x1C49, + 0x1C4D, 0x1C7D, + 0x1C80, 0x1C88, + 0x1C90, 0x1CBA, + 0x1CBD, 0x1CBF, + 0x1CE9, 0x1CEC, + 0x1CEE, 0x1CF3, + 0x1CF5, 0x1CF6, + 0x1CFA, 0x1CFA, + 0x1D00, 0x1DBF, + 0x1DE7, 0x1DF4, + 0x1E00, 0x1F15, + 0x1F18, 0x1F1D, + 0x1F20, 0x1F45, + 0x1F48, 0x1F4D, + 0x1F50, 0x1F57, + 0x1F59, 0x1F59, + 0x1F5B, 0x1F5B, + 0x1F5D, 0x1F5D, + 0x1F5F, 0x1F7D, + 0x1F80, 0x1FB4, + 0x1FB6, 0x1FBC, + 0x1FBE, 0x1FBE, + 0x1FC2, 0x1FC4, + 0x1FC6, 0x1FCC, + 0x1FD0, 0x1FD3, + 0x1FD6, 0x1FDB, + 0x1FE0, 0x1FEC, + 0x1FF2, 0x1FF4, + 0x1FF6, 0x1FFC, + 0x2071, 0x2071, + 0x207F, 0x207F, + 0x2090, 0x209C, + 0x2102, 0x2102, + 0x2107, 0x2107, + 0x210A, 0x2113, + 0x2115, 0x2115, + 0x2119, 0x211D, + 0x2124, 0x2124, + 0x2126, 0x2126, + 0x2128, 0x2128, + 0x212A, 0x212D, + 0x212F, 0x2139, + 0x213C, 0x213F, + 0x2145, 0x2149, + 0x214E, 0x214E, + 0x2160, 0x2188, + 0x24B6, 0x24E9, + 0x2C00, 0x2CE4, + 0x2CEB, 0x2CEE, + 0x2CF2, 0x2CF3, + 0x2D00, 0x2D25, + 0x2D27, 0x2D27, + 0x2D2D, 0x2D2D, + 0x2D30, 0x2D67, + 0x2D6F, 0x2D6F, + 0x2D80, 0x2D96, + 0x2DA0, 0x2DA6, + 0x2DA8, 0x2DAE, + 0x2DB0, 0x2DB6, + 0x2DB8, 0x2DBE, + 0x2DC0, 0x2DC6, + 0x2DC8, 0x2DCE, + 0x2DD0, 0x2DD6, + 0x2DD8, 0x2DDE, + 0x2DE0, 0x2DFF, + 0x2E2F, 0x2E2F, + 0x3005, 0x3007, + 0x3021, 0x3029, + 0x3031, 0x3035, + 0x3038, 0x303C, + 0x3041, 0x3096, + 0x309D, 0x309F, + 0x30A1, 0x30FA, + 0x30FC, 0x30FF, + 0x3105, 0x312F, + 0x3131, 0x318E, + 0x31A0, 0x31BF, + 0x31F0, 0x31FF, + 0x3400, 0x4DBF, + 0x4E00, 0xA48C, + 0xA4D0, 0xA4FD, + 0xA500, 0xA60C, + 0xA610, 0xA62B, + 0xA640, 0xA66E, + 0xA674, 0xA67B, + 0xA67F, 0xA6EF, + 0xA717, 0xA71F, + 0xA722, 0xA788, + 0xA78B, 0xA7CA, + 0xA7D0, 0xA7D1, + 0xA7D3, 0xA7D3, + 0xA7D5, 0xA7D9, + 0xA7F2, 0xA805, + 0xA807, 0xA827, + 0xA840, 0xA873, + 0xA880, 0xA8C3, + 0xA8C5, 0xA8C5, + 0xA8D0, 0xA8D9, + 0xA8F2, 0xA8F7, + 0xA8FB, 0xA8FB, + 0xA8FD, 0xA92A, + 0xA930, 0xA952, + 0xA960, 0xA97C, + 0xA980, 0xA9B2, + 0xA9B4, 0xA9BF, + 0xA9CF, 0xA9D9, + 0xA9E0, 0xA9FE, + 0xAA00, 0xAA36, + 0xAA40, 0xAA4D, + 0xAA50, 0xAA59, + 0xAA60, 0xAA76, + 0xAA7A, 0xAABE, + 0xAAC0, 0xAAC0, + 0xAAC2, 0xAAC2, + 0xAADB, 0xAADD, + 0xAAE0, 0xAAEF, + 0xAAF2, 0xAAF5, + 0xAB01, 0xAB06, + 0xAB09, 0xAB0E, + 0xAB11, 0xAB16, + 0xAB20, 0xAB26, + 0xAB28, 0xAB2E, + 0xAB30, 0xAB5A, + 0xAB5C, 0xAB69, + 0xAB70, 0xABEA, + 0xABF0, 0xABF9, + 0xAC00, 0xD7A3, + 0xD7B0, 0xD7C6, + 0xD7CB, 0xD7FB, + 0xF900, 0xFA6D, + 0xFA70, 0xFAD9, + 0xFB00, 0xFB06, + 0xFB13, 0xFB17, + 0xFB1D, 0xFB28, + 0xFB2A, 0xFB36, + 0xFB38, 0xFB3C, + 0xFB3E, 0xFB3E, + 0xFB40, 0xFB41, + 0xFB43, 0xFB44, + 0xFB46, 0xFBB1, + 0xFBD3, 0xFD3D, + 0xFD50, 0xFD8F, + 0xFD92, 0xFDC7, + 0xFDF0, 0xFDFB, + 0xFE70, 0xFE74, + 0xFE76, 0xFEFC, + 0xFF10, 0xFF19, + 0xFF21, 0xFF3A, + 0xFF41, 0xFF5A, + 0xFF66, 0xFFBE, + 0xFFC2, 0xFFC7, + 0xFFCA, 0xFFCF, + 0xFFD2, 0xFFD7, + 0xFFDA, 0xFFDC, + 0x10000, 0x1000B, + 0x1000D, 0x10026, + 0x10028, 0x1003A, + 0x1003C, 0x1003D, + 0x1003F, 0x1004D, + 0x10050, 0x1005D, + 0x10080, 0x100FA, + 0x10140, 0x10174, + 0x10280, 0x1029C, + 0x102A0, 0x102D0, + 0x10300, 0x1031F, + 0x1032D, 0x1034A, + 0x10350, 0x1037A, + 0x10380, 0x1039D, + 0x103A0, 0x103C3, + 0x103C8, 0x103CF, + 0x103D1, 0x103D5, + 0x10400, 0x1049D, + 0x104A0, 0x104A9, + 0x104B0, 0x104D3, + 0x104D8, 0x104FB, + 0x10500, 0x10527, + 0x10530, 0x10563, + 0x10570, 0x1057A, + 0x1057C, 0x1058A, + 0x1058C, 0x10592, + 0x10594, 0x10595, + 0x10597, 0x105A1, + 0x105A3, 0x105B1, + 0x105B3, 0x105B9, + 0x105BB, 0x105BC, + 0x10600, 0x10736, + 0x10740, 0x10755, + 0x10760, 0x10767, + 0x10780, 0x10785, + 0x10787, 0x107B0, + 0x107B2, 0x107BA, + 0x10800, 0x10805, + 0x10808, 0x10808, + 0x1080A, 0x10835, + 0x10837, 0x10838, + 0x1083C, 0x1083C, + 0x1083F, 0x10855, + 0x10860, 0x10876, + 0x10880, 0x1089E, + 0x108E0, 0x108F2, + 0x108F4, 0x108F5, + 0x10900, 0x10915, + 0x10920, 0x10939, + 0x10980, 0x109B7, + 0x109BE, 0x109BF, + 0x10A00, 0x10A03, + 0x10A05, 0x10A06, + 0x10A0C, 0x10A13, + 0x10A15, 0x10A17, + 0x10A19, 0x10A35, + 0x10A60, 0x10A7C, + 0x10A80, 0x10A9C, + 0x10AC0, 0x10AC7, + 0x10AC9, 0x10AE4, + 0x10B00, 0x10B35, + 0x10B40, 0x10B55, + 0x10B60, 0x10B72, + 0x10B80, 0x10B91, + 0x10C00, 0x10C48, + 0x10C80, 0x10CB2, + 0x10CC0, 0x10CF2, + 0x10D00, 0x10D27, + 0x10D30, 0x10D39, + 0x10E80, 0x10EA9, + 0x10EAB, 0x10EAC, + 0x10EB0, 0x10EB1, + 0x10F00, 0x10F1C, + 0x10F27, 0x10F27, + 0x10F30, 0x10F45, + 0x10F70, 0x10F81, + 0x10FB0, 0x10FC4, + 0x10FE0, 0x10FF6, + 0x11000, 0x11045, + 0x11066, 0x1106F, + 0x11071, 0x11075, + 0x11080, 0x110B8, + 0x110C2, 0x110C2, + 0x110D0, 0x110E8, + 0x110F0, 0x110F9, + 0x11100, 0x11132, + 0x11136, 0x1113F, + 0x11144, 0x11147, + 0x11150, 0x11172, + 0x11176, 0x11176, + 0x11180, 0x111BF, + 0x111C1, 0x111C4, + 0x111CE, 0x111DA, + 0x111DC, 0x111DC, + 0x11200, 0x11211, + 0x11213, 0x11234, + 0x11237, 0x11237, + 0x1123E, 0x11241, + 0x11280, 0x11286, + 0x11288, 0x11288, + 0x1128A, 0x1128D, + 0x1128F, 0x1129D, + 0x1129F, 0x112A8, + 0x112B0, 0x112E8, + 0x112F0, 0x112F9, + 0x11300, 0x11303, + 0x11305, 0x1130C, + 0x1130F, 0x11310, + 0x11313, 0x11328, + 0x1132A, 0x11330, + 0x11332, 0x11333, + 0x11335, 0x11339, + 0x1133D, 0x11344, + 0x11347, 0x11348, + 0x1134B, 0x1134C, + 0x11350, 0x11350, + 0x11357, 0x11357, + 0x1135D, 0x11363, + 0x11400, 0x11441, + 0x11443, 0x11445, + 0x11447, 0x1144A, + 0x11450, 0x11459, + 0x1145F, 0x11461, + 0x11480, 0x114C1, + 0x114C4, 0x114C5, + 0x114C7, 0x114C7, + 0x114D0, 0x114D9, + 0x11580, 0x115B5, + 0x115B8, 0x115BE, + 0x115D8, 0x115DD, + 0x11600, 0x1163E, + 0x11640, 0x11640, + 0x11644, 0x11644, + 0x11650, 0x11659, + 0x11680, 0x116B5, + 0x116B8, 0x116B8, + 0x116C0, 0x116C9, + 0x11700, 0x1171A, + 0x1171D, 0x1172A, + 0x11730, 0x11739, + 0x11740, 0x11746, + 0x11800, 0x11838, + 0x118A0, 0x118E9, + 0x118FF, 0x11906, + 0x11909, 0x11909, + 0x1190C, 0x11913, + 0x11915, 0x11916, + 0x11918, 0x11935, + 0x11937, 0x11938, + 0x1193B, 0x1193C, + 0x1193F, 0x11942, + 0x11950, 0x11959, + 0x119A0, 0x119A7, + 0x119AA, 0x119D7, + 0x119DA, 0x119DF, + 0x119E1, 0x119E1, + 0x119E3, 0x119E4, + 0x11A00, 0x11A32, + 0x11A35, 0x11A3E, + 0x11A50, 0x11A97, + 0x11A9D, 0x11A9D, + 0x11AB0, 0x11AF8, + 0x11C00, 0x11C08, + 0x11C0A, 0x11C36, + 0x11C38, 0x11C3E, + 0x11C40, 0x11C40, + 0x11C50, 0x11C59, + 0x11C72, 0x11C8F, + 0x11C92, 0x11CA7, + 0x11CA9, 0x11CB6, + 0x11D00, 0x11D06, + 0x11D08, 0x11D09, + 0x11D0B, 0x11D36, + 0x11D3A, 0x11D3A, + 0x11D3C, 0x11D3D, + 0x11D3F, 0x11D41, + 0x11D43, 0x11D43, + 0x11D46, 0x11D47, + 0x11D50, 0x11D59, + 0x11D60, 0x11D65, + 0x11D67, 0x11D68, + 0x11D6A, 0x11D8E, + 0x11D90, 0x11D91, + 0x11D93, 0x11D96, + 0x11D98, 0x11D98, + 0x11DA0, 0x11DA9, + 0x11EE0, 0x11EF6, + 0x11F00, 0x11F10, + 0x11F12, 0x11F3A, + 0x11F3E, 0x11F40, + 0x11F50, 0x11F59, + 0x11FB0, 0x11FB0, + 0x12000, 0x12399, + 0x12400, 0x1246E, + 0x12480, 0x12543, + 0x12F90, 0x12FF0, + 0x13000, 0x1342F, + 0x13441, 0x13446, + 0x14400, 0x14646, + 0x16800, 0x16A38, + 0x16A40, 0x16A5E, + 0x16A60, 0x16A69, + 0x16A70, 0x16ABE, + 0x16AC0, 0x16AC9, + 0x16AD0, 0x16AED, + 0x16B00, 0x16B2F, + 0x16B40, 0x16B43, + 0x16B50, 0x16B59, + 0x16B63, 0x16B77, + 0x16B7D, 0x16B8F, + 0x16E40, 0x16E7F, + 0x16F00, 0x16F4A, + 0x16F4F, 0x16F87, + 0x16F8F, 0x16F9F, + 0x16FE0, 0x16FE1, + 0x16FE3, 0x16FE3, + 0x16FF0, 0x16FF1, + 0x17000, 0x187F7, + 0x18800, 0x18CD5, + 0x18D00, 0x18D08, + 0x1AFF0, 0x1AFF3, + 0x1AFF5, 0x1AFFB, + 0x1AFFD, 0x1AFFE, + 0x1B000, 0x1B122, + 0x1B132, 0x1B132, + 0x1B150, 0x1B152, + 0x1B155, 0x1B155, + 0x1B164, 0x1B167, + 0x1B170, 0x1B2FB, + 0x1BC00, 0x1BC6A, + 0x1BC70, 0x1BC7C, + 0x1BC80, 0x1BC88, + 0x1BC90, 0x1BC99, + 0x1BC9E, 0x1BC9E, + 0x1D400, 0x1D454, + 0x1D456, 0x1D49C, + 0x1D49E, 0x1D49F, + 0x1D4A2, 0x1D4A2, + 0x1D4A5, 0x1D4A6, + 0x1D4A9, 0x1D4AC, + 0x1D4AE, 0x1D4B9, + 0x1D4BB, 0x1D4BB, + 0x1D4BD, 0x1D4C3, + 0x1D4C5, 0x1D505, + 0x1D507, 0x1D50A, + 0x1D50D, 0x1D514, + 0x1D516, 0x1D51C, + 0x1D51E, 0x1D539, + 0x1D53B, 0x1D53E, + 0x1D540, 0x1D544, + 0x1D546, 0x1D546, + 0x1D54A, 0x1D550, + 0x1D552, 0x1D6A5, + 0x1D6A8, 0x1D6C0, + 0x1D6C2, 0x1D6DA, + 0x1D6DC, 0x1D6FA, + 0x1D6FC, 0x1D714, + 0x1D716, 0x1D734, + 0x1D736, 0x1D74E, + 0x1D750, 0x1D76E, + 0x1D770, 0x1D788, + 0x1D78A, 0x1D7A8, + 0x1D7AA, 0x1D7C2, + 0x1D7C4, 0x1D7CB, + 0x1D7CE, 0x1D7FF, + 0x1DF00, 0x1DF1E, + 0x1DF25, 0x1DF2A, + 0x1E000, 0x1E006, + 0x1E008, 0x1E018, + 0x1E01B, 0x1E021, + 0x1E023, 0x1E024, + 0x1E026, 0x1E02A, + 0x1E030, 0x1E06D, + 0x1E08F, 0x1E08F, + 0x1E100, 0x1E12C, + 0x1E137, 0x1E13D, + 0x1E140, 0x1E149, + 0x1E14E, 0x1E14E, + 0x1E290, 0x1E2AD, + 0x1E2C0, 0x1E2EB, + 0x1E2F0, 0x1E2F9, + 0x1E4D0, 0x1E4EB, + 0x1E4F0, 0x1E4F9, + 0x1E7E0, 0x1E7E6, + 0x1E7E8, 0x1E7EB, + 0x1E7ED, 0x1E7EE, + 0x1E7F0, 0x1E7FE, + 0x1E800, 0x1E8C4, + 0x1E900, 0x1E943, + 0x1E947, 0x1E947, + 0x1E94B, 0x1E94B, + 0x1E950, 0x1E959, + 0x1EE00, 0x1EE03, + 0x1EE05, 0x1EE1F, + 0x1EE21, 0x1EE22, + 0x1EE24, 0x1EE24, + 0x1EE27, 0x1EE27, + 0x1EE29, 0x1EE32, + 0x1EE34, 0x1EE37, + 0x1EE39, 0x1EE39, + 0x1EE3B, 0x1EE3B, + 0x1EE42, 0x1EE42, + 0x1EE47, 0x1EE47, + 0x1EE49, 0x1EE49, + 0x1EE4B, 0x1EE4B, + 0x1EE4D, 0x1EE4F, + 0x1EE51, 0x1EE52, + 0x1EE54, 0x1EE54, + 0x1EE57, 0x1EE57, + 0x1EE59, 0x1EE59, + 0x1EE5B, 0x1EE5B, + 0x1EE5D, 0x1EE5D, + 0x1EE5F, 0x1EE5F, + 0x1EE61, 0x1EE62, + 0x1EE64, 0x1EE64, + 0x1EE67, 0x1EE6A, + 0x1EE6C, 0x1EE72, + 0x1EE74, 0x1EE77, + 0x1EE79, 0x1EE7C, + 0x1EE7E, 0x1EE7E, + 0x1EE80, 0x1EE89, + 0x1EE8B, 0x1EE9B, + 0x1EEA1, 0x1EEA3, + 0x1EEA5, 0x1EEA9, + 0x1EEAB, 0x1EEBB, + 0x1F130, 0x1F149, + 0x1F150, 0x1F169, + 0x1F170, 0x1F189, + 0x1FBF0, 0x1FBF9, + 0x20000, 0x2A6DF, + 0x2A700, 0x2B739, + 0x2B740, 0x2B81D, + 0x2B820, 0x2CEA1, + 0x2CEB0, 0x2EBE0, + 0x2F800, 0x2FA1D, + 0x30000, 0x3134A, + 0x31350, 0x323AF, +}; + +#define UNICODE_ISUPPER_CODEPOINTS_LENGTH 1296 +static unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_CODEPOINTS_LENGTH] = { + 0x100, 0x100, + 0x102, 0x102, + 0x104, 0x104, + 0x106, 0x106, + 0x108, 0x108, + 0x10A, 0x10A, + 0x10C, 0x10C, + 0x10E, 0x10E, + 0x110, 0x110, + 0x112, 0x112, + 0x114, 0x114, + 0x116, 0x116, + 0x118, 0x118, + 0x11A, 0x11A, + 0x11C, 0x11C, + 0x11E, 0x11E, + 0x120, 0x120, + 0x122, 0x122, + 0x124, 0x124, + 0x126, 0x126, + 0x128, 0x128, + 0x12A, 0x12A, + 0x12C, 0x12C, + 0x12E, 0x12E, + 0x130, 0x130, + 0x132, 0x132, + 0x134, 0x134, + 0x136, 0x136, + 0x139, 0x139, + 0x13B, 0x13B, + 0x13D, 0x13D, + 0x13F, 0x13F, + 0x141, 0x141, + 0x143, 0x143, + 0x145, 0x145, + 0x147, 0x147, + 0x14A, 0x14A, + 0x14C, 0x14C, + 0x14E, 0x14E, + 0x150, 0x150, + 0x152, 0x152, + 0x154, 0x154, + 0x156, 0x156, + 0x158, 0x158, + 0x15A, 0x15A, + 0x15C, 0x15C, + 0x15E, 0x15E, + 0x160, 0x160, + 0x162, 0x162, + 0x164, 0x164, + 0x166, 0x166, + 0x168, 0x168, + 0x16A, 0x16A, + 0x16C, 0x16C, + 0x16E, 0x16E, + 0x170, 0x170, + 0x172, 0x172, + 0x174, 0x174, + 0x176, 0x176, + 0x178, 0x179, + 0x17B, 0x17B, + 0x17D, 0x17D, + 0x181, 0x182, + 0x184, 0x184, + 0x186, 0x187, + 0x189, 0x18B, + 0x18E, 0x191, + 0x193, 0x194, + 0x196, 0x198, + 0x19C, 0x19D, + 0x19F, 0x1A0, + 0x1A2, 0x1A2, + 0x1A4, 0x1A4, + 0x1A6, 0x1A7, + 0x1A9, 0x1A9, + 0x1AC, 0x1AC, + 0x1AE, 0x1AF, + 0x1B1, 0x1B3, + 0x1B5, 0x1B5, + 0x1B7, 0x1B8, + 0x1BC, 0x1BC, + 0x1C4, 0x1C4, + 0x1C7, 0x1C7, + 0x1CA, 0x1CA, + 0x1CD, 0x1CD, + 0x1CF, 0x1CF, + 0x1D1, 0x1D1, + 0x1D3, 0x1D3, + 0x1D5, 0x1D5, + 0x1D7, 0x1D7, + 0x1D9, 0x1D9, + 0x1DB, 0x1DB, + 0x1DE, 0x1DE, + 0x1E0, 0x1E0, + 0x1E2, 0x1E2, + 0x1E4, 0x1E4, + 0x1E6, 0x1E6, + 0x1E8, 0x1E8, + 0x1EA, 0x1EA, + 0x1EC, 0x1EC, + 0x1EE, 0x1EE, + 0x1F1, 0x1F1, + 0x1F4, 0x1F4, + 0x1F6, 0x1F8, + 0x1FA, 0x1FA, + 0x1FC, 0x1FC, + 0x1FE, 0x1FE, + 0x200, 0x200, + 0x202, 0x202, + 0x204, 0x204, + 0x206, 0x206, + 0x208, 0x208, + 0x20A, 0x20A, + 0x20C, 0x20C, + 0x20E, 0x20E, + 0x210, 0x210, + 0x212, 0x212, + 0x214, 0x214, + 0x216, 0x216, + 0x218, 0x218, + 0x21A, 0x21A, + 0x21C, 0x21C, + 0x21E, 0x21E, + 0x220, 0x220, + 0x222, 0x222, + 0x224, 0x224, + 0x226, 0x226, + 0x228, 0x228, + 0x22A, 0x22A, + 0x22C, 0x22C, + 0x22E, 0x22E, + 0x230, 0x230, + 0x232, 0x232, + 0x23A, 0x23B, + 0x23D, 0x23E, + 0x241, 0x241, + 0x243, 0x246, + 0x248, 0x248, + 0x24A, 0x24A, + 0x24C, 0x24C, + 0x24E, 0x24E, + 0x370, 0x370, + 0x372, 0x372, + 0x376, 0x376, + 0x37F, 0x37F, + 0x386, 0x386, + 0x388, 0x38A, + 0x38C, 0x38C, + 0x38E, 0x38F, + 0x391, 0x3A1, + 0x3A3, 0x3AB, + 0x3CF, 0x3CF, + 0x3D2, 0x3D4, + 0x3D8, 0x3D8, + 0x3DA, 0x3DA, + 0x3DC, 0x3DC, + 0x3DE, 0x3DE, + 0x3E0, 0x3E0, + 0x3E2, 0x3E2, + 0x3E4, 0x3E4, + 0x3E6, 0x3E6, + 0x3E8, 0x3E8, + 0x3EA, 0x3EA, + 0x3EC, 0x3EC, + 0x3EE, 0x3EE, + 0x3F4, 0x3F4, + 0x3F7, 0x3F7, + 0x3F9, 0x3FA, + 0x3FD, 0x42F, + 0x460, 0x460, + 0x462, 0x462, + 0x464, 0x464, + 0x466, 0x466, + 0x468, 0x468, + 0x46A, 0x46A, + 0x46C, 0x46C, + 0x46E, 0x46E, + 0x470, 0x470, + 0x472, 0x472, + 0x474, 0x474, + 0x476, 0x476, + 0x478, 0x478, + 0x47A, 0x47A, + 0x47C, 0x47C, + 0x47E, 0x47E, + 0x480, 0x480, + 0x48A, 0x48A, + 0x48C, 0x48C, + 0x48E, 0x48E, + 0x490, 0x490, + 0x492, 0x492, + 0x494, 0x494, + 0x496, 0x496, + 0x498, 0x498, + 0x49A, 0x49A, + 0x49C, 0x49C, + 0x49E, 0x49E, + 0x4A0, 0x4A0, + 0x4A2, 0x4A2, + 0x4A4, 0x4A4, + 0x4A6, 0x4A6, + 0x4A8, 0x4A8, + 0x4AA, 0x4AA, + 0x4AC, 0x4AC, + 0x4AE, 0x4AE, + 0x4B0, 0x4B0, + 0x4B2, 0x4B2, + 0x4B4, 0x4B4, + 0x4B6, 0x4B6, + 0x4B8, 0x4B8, + 0x4BA, 0x4BA, + 0x4BC, 0x4BC, + 0x4BE, 0x4BE, + 0x4C0, 0x4C1, + 0x4C3, 0x4C3, + 0x4C5, 0x4C5, + 0x4C7, 0x4C7, + 0x4C9, 0x4C9, + 0x4CB, 0x4CB, + 0x4CD, 0x4CD, + 0x4D0, 0x4D0, + 0x4D2, 0x4D2, + 0x4D4, 0x4D4, + 0x4D6, 0x4D6, + 0x4D8, 0x4D8, + 0x4DA, 0x4DA, + 0x4DC, 0x4DC, + 0x4DE, 0x4DE, + 0x4E0, 0x4E0, + 0x4E2, 0x4E2, + 0x4E4, 0x4E4, + 0x4E6, 0x4E6, + 0x4E8, 0x4E8, + 0x4EA, 0x4EA, + 0x4EC, 0x4EC, + 0x4EE, 0x4EE, + 0x4F0, 0x4F0, + 0x4F2, 0x4F2, + 0x4F4, 0x4F4, + 0x4F6, 0x4F6, + 0x4F8, 0x4F8, + 0x4FA, 0x4FA, + 0x4FC, 0x4FC, + 0x4FE, 0x4FE, + 0x500, 0x500, + 0x502, 0x502, + 0x504, 0x504, + 0x506, 0x506, + 0x508, 0x508, + 0x50A, 0x50A, + 0x50C, 0x50C, + 0x50E, 0x50E, + 0x510, 0x510, + 0x512, 0x512, + 0x514, 0x514, + 0x516, 0x516, + 0x518, 0x518, + 0x51A, 0x51A, + 0x51C, 0x51C, + 0x51E, 0x51E, + 0x520, 0x520, + 0x522, 0x522, + 0x524, 0x524, + 0x526, 0x526, + 0x528, 0x528, + 0x52A, 0x52A, + 0x52C, 0x52C, + 0x52E, 0x52E, + 0x531, 0x556, + 0x10A0, 0x10C5, + 0x10C7, 0x10C7, + 0x10CD, 0x10CD, + 0x13A0, 0x13F5, + 0x1C90, 0x1CBA, + 0x1CBD, 0x1CBF, + 0x1E00, 0x1E00, + 0x1E02, 0x1E02, + 0x1E04, 0x1E04, + 0x1E06, 0x1E06, + 0x1E08, 0x1E08, + 0x1E0A, 0x1E0A, + 0x1E0C, 0x1E0C, + 0x1E0E, 0x1E0E, + 0x1E10, 0x1E10, + 0x1E12, 0x1E12, + 0x1E14, 0x1E14, + 0x1E16, 0x1E16, + 0x1E18, 0x1E18, + 0x1E1A, 0x1E1A, + 0x1E1C, 0x1E1C, + 0x1E1E, 0x1E1E, + 0x1E20, 0x1E20, + 0x1E22, 0x1E22, + 0x1E24, 0x1E24, + 0x1E26, 0x1E26, + 0x1E28, 0x1E28, + 0x1E2A, 0x1E2A, + 0x1E2C, 0x1E2C, + 0x1E2E, 0x1E2E, + 0x1E30, 0x1E30, + 0x1E32, 0x1E32, + 0x1E34, 0x1E34, + 0x1E36, 0x1E36, + 0x1E38, 0x1E38, + 0x1E3A, 0x1E3A, + 0x1E3C, 0x1E3C, + 0x1E3E, 0x1E3E, + 0x1E40, 0x1E40, + 0x1E42, 0x1E42, + 0x1E44, 0x1E44, + 0x1E46, 0x1E46, + 0x1E48, 0x1E48, + 0x1E4A, 0x1E4A, + 0x1E4C, 0x1E4C, + 0x1E4E, 0x1E4E, + 0x1E50, 0x1E50, + 0x1E52, 0x1E52, + 0x1E54, 0x1E54, + 0x1E56, 0x1E56, + 0x1E58, 0x1E58, + 0x1E5A, 0x1E5A, + 0x1E5C, 0x1E5C, + 0x1E5E, 0x1E5E, + 0x1E60, 0x1E60, + 0x1E62, 0x1E62, + 0x1E64, 0x1E64, + 0x1E66, 0x1E66, + 0x1E68, 0x1E68, + 0x1E6A, 0x1E6A, + 0x1E6C, 0x1E6C, + 0x1E6E, 0x1E6E, + 0x1E70, 0x1E70, + 0x1E72, 0x1E72, + 0x1E74, 0x1E74, + 0x1E76, 0x1E76, + 0x1E78, 0x1E78, + 0x1E7A, 0x1E7A, + 0x1E7C, 0x1E7C, + 0x1E7E, 0x1E7E, + 0x1E80, 0x1E80, + 0x1E82, 0x1E82, + 0x1E84, 0x1E84, + 0x1E86, 0x1E86, + 0x1E88, 0x1E88, + 0x1E8A, 0x1E8A, + 0x1E8C, 0x1E8C, + 0x1E8E, 0x1E8E, + 0x1E90, 0x1E90, + 0x1E92, 0x1E92, + 0x1E94, 0x1E94, + 0x1E9E, 0x1E9E, + 0x1EA0, 0x1EA0, + 0x1EA2, 0x1EA2, + 0x1EA4, 0x1EA4, + 0x1EA6, 0x1EA6, + 0x1EA8, 0x1EA8, + 0x1EAA, 0x1EAA, + 0x1EAC, 0x1EAC, + 0x1EAE, 0x1EAE, + 0x1EB0, 0x1EB0, + 0x1EB2, 0x1EB2, + 0x1EB4, 0x1EB4, + 0x1EB6, 0x1EB6, + 0x1EB8, 0x1EB8, + 0x1EBA, 0x1EBA, + 0x1EBC, 0x1EBC, + 0x1EBE, 0x1EBE, + 0x1EC0, 0x1EC0, + 0x1EC2, 0x1EC2, + 0x1EC4, 0x1EC4, + 0x1EC6, 0x1EC6, + 0x1EC8, 0x1EC8, + 0x1ECA, 0x1ECA, + 0x1ECC, 0x1ECC, + 0x1ECE, 0x1ECE, + 0x1ED0, 0x1ED0, + 0x1ED2, 0x1ED2, + 0x1ED4, 0x1ED4, + 0x1ED6, 0x1ED6, + 0x1ED8, 0x1ED8, + 0x1EDA, 0x1EDA, + 0x1EDC, 0x1EDC, + 0x1EDE, 0x1EDE, + 0x1EE0, 0x1EE0, + 0x1EE2, 0x1EE2, + 0x1EE4, 0x1EE4, + 0x1EE6, 0x1EE6, + 0x1EE8, 0x1EE8, + 0x1EEA, 0x1EEA, + 0x1EEC, 0x1EEC, + 0x1EEE, 0x1EEE, + 0x1EF0, 0x1EF0, + 0x1EF2, 0x1EF2, + 0x1EF4, 0x1EF4, + 0x1EF6, 0x1EF6, + 0x1EF8, 0x1EF8, + 0x1EFA, 0x1EFA, + 0x1EFC, 0x1EFC, + 0x1EFE, 0x1EFE, + 0x1F08, 0x1F0F, + 0x1F18, 0x1F1D, + 0x1F28, 0x1F2F, + 0x1F38, 0x1F3F, + 0x1F48, 0x1F4D, + 0x1F59, 0x1F59, + 0x1F5B, 0x1F5B, + 0x1F5D, 0x1F5D, + 0x1F5F, 0x1F5F, + 0x1F68, 0x1F6F, + 0x1FB8, 0x1FBB, + 0x1FC8, 0x1FCB, + 0x1FD8, 0x1FDB, + 0x1FE8, 0x1FEC, + 0x1FF8, 0x1FFB, + 0x2102, 0x2102, + 0x2107, 0x2107, + 0x210B, 0x210D, + 0x2110, 0x2112, + 0x2115, 0x2115, + 0x2119, 0x211D, + 0x2124, 0x2124, + 0x2126, 0x2126, + 0x2128, 0x2128, + 0x212A, 0x212D, + 0x2130, 0x2133, + 0x213E, 0x213F, + 0x2145, 0x2145, + 0x2160, 0x216F, + 0x2183, 0x2183, + 0x24B6, 0x24CF, + 0x2C00, 0x2C2F, + 0x2C60, 0x2C60, + 0x2C62, 0x2C64, + 0x2C67, 0x2C67, + 0x2C69, 0x2C69, + 0x2C6B, 0x2C6B, + 0x2C6D, 0x2C70, + 0x2C72, 0x2C72, + 0x2C75, 0x2C75, + 0x2C7E, 0x2C80, + 0x2C82, 0x2C82, + 0x2C84, 0x2C84, + 0x2C86, 0x2C86, + 0x2C88, 0x2C88, + 0x2C8A, 0x2C8A, + 0x2C8C, 0x2C8C, + 0x2C8E, 0x2C8E, + 0x2C90, 0x2C90, + 0x2C92, 0x2C92, + 0x2C94, 0x2C94, + 0x2C96, 0x2C96, + 0x2C98, 0x2C98, + 0x2C9A, 0x2C9A, + 0x2C9C, 0x2C9C, + 0x2C9E, 0x2C9E, + 0x2CA0, 0x2CA0, + 0x2CA2, 0x2CA2, + 0x2CA4, 0x2CA4, + 0x2CA6, 0x2CA6, + 0x2CA8, 0x2CA8, + 0x2CAA, 0x2CAA, + 0x2CAC, 0x2CAC, + 0x2CAE, 0x2CAE, + 0x2CB0, 0x2CB0, + 0x2CB2, 0x2CB2, + 0x2CB4, 0x2CB4, + 0x2CB6, 0x2CB6, + 0x2CB8, 0x2CB8, + 0x2CBA, 0x2CBA, + 0x2CBC, 0x2CBC, + 0x2CBE, 0x2CBE, + 0x2CC0, 0x2CC0, + 0x2CC2, 0x2CC2, + 0x2CC4, 0x2CC4, + 0x2CC6, 0x2CC6, + 0x2CC8, 0x2CC8, + 0x2CCA, 0x2CCA, + 0x2CCC, 0x2CCC, + 0x2CCE, 0x2CCE, + 0x2CD0, 0x2CD0, + 0x2CD2, 0x2CD2, + 0x2CD4, 0x2CD4, + 0x2CD6, 0x2CD6, + 0x2CD8, 0x2CD8, + 0x2CDA, 0x2CDA, + 0x2CDC, 0x2CDC, + 0x2CDE, 0x2CDE, + 0x2CE0, 0x2CE0, + 0x2CE2, 0x2CE2, + 0x2CEB, 0x2CEB, + 0x2CED, 0x2CED, + 0x2CF2, 0x2CF2, + 0xA640, 0xA640, + 0xA642, 0xA642, + 0xA644, 0xA644, + 0xA646, 0xA646, + 0xA648, 0xA648, + 0xA64A, 0xA64A, + 0xA64C, 0xA64C, + 0xA64E, 0xA64E, + 0xA650, 0xA650, + 0xA652, 0xA652, + 0xA654, 0xA654, + 0xA656, 0xA656, + 0xA658, 0xA658, + 0xA65A, 0xA65A, + 0xA65C, 0xA65C, + 0xA65E, 0xA65E, + 0xA660, 0xA660, + 0xA662, 0xA662, + 0xA664, 0xA664, + 0xA666, 0xA666, + 0xA668, 0xA668, + 0xA66A, 0xA66A, + 0xA66C, 0xA66C, + 0xA680, 0xA680, + 0xA682, 0xA682, + 0xA684, 0xA684, + 0xA686, 0xA686, + 0xA688, 0xA688, + 0xA68A, 0xA68A, + 0xA68C, 0xA68C, + 0xA68E, 0xA68E, + 0xA690, 0xA690, + 0xA692, 0xA692, + 0xA694, 0xA694, + 0xA696, 0xA696, + 0xA698, 0xA698, + 0xA69A, 0xA69A, + 0xA722, 0xA722, + 0xA724, 0xA724, + 0xA726, 0xA726, + 0xA728, 0xA728, + 0xA72A, 0xA72A, + 0xA72C, 0xA72C, + 0xA72E, 0xA72E, + 0xA732, 0xA732, + 0xA734, 0xA734, + 0xA736, 0xA736, + 0xA738, 0xA738, + 0xA73A, 0xA73A, + 0xA73C, 0xA73C, + 0xA73E, 0xA73E, + 0xA740, 0xA740, + 0xA742, 0xA742, + 0xA744, 0xA744, + 0xA746, 0xA746, + 0xA748, 0xA748, + 0xA74A, 0xA74A, + 0xA74C, 0xA74C, + 0xA74E, 0xA74E, + 0xA750, 0xA750, + 0xA752, 0xA752, + 0xA754, 0xA754, + 0xA756, 0xA756, + 0xA758, 0xA758, + 0xA75A, 0xA75A, + 0xA75C, 0xA75C, + 0xA75E, 0xA75E, + 0xA760, 0xA760, + 0xA762, 0xA762, + 0xA764, 0xA764, + 0xA766, 0xA766, + 0xA768, 0xA768, + 0xA76A, 0xA76A, + 0xA76C, 0xA76C, + 0xA76E, 0xA76E, + 0xA779, 0xA779, + 0xA77B, 0xA77B, + 0xA77D, 0xA77E, + 0xA780, 0xA780, + 0xA782, 0xA782, + 0xA784, 0xA784, + 0xA786, 0xA786, + 0xA78B, 0xA78B, + 0xA78D, 0xA78D, + 0xA790, 0xA790, + 0xA792, 0xA792, + 0xA796, 0xA796, + 0xA798, 0xA798, + 0xA79A, 0xA79A, + 0xA79C, 0xA79C, + 0xA79E, 0xA79E, + 0xA7A0, 0xA7A0, + 0xA7A2, 0xA7A2, + 0xA7A4, 0xA7A4, + 0xA7A6, 0xA7A6, + 0xA7A8, 0xA7A8, + 0xA7AA, 0xA7AE, + 0xA7B0, 0xA7B4, + 0xA7B6, 0xA7B6, + 0xA7B8, 0xA7B8, + 0xA7BA, 0xA7BA, + 0xA7BC, 0xA7BC, + 0xA7BE, 0xA7BE, + 0xA7C0, 0xA7C0, + 0xA7C2, 0xA7C2, + 0xA7C4, 0xA7C7, + 0xA7C9, 0xA7C9, + 0xA7D0, 0xA7D0, + 0xA7D6, 0xA7D6, + 0xA7D8, 0xA7D8, + 0xA7F5, 0xA7F5, + 0xFF21, 0xFF3A, + 0x10400, 0x10427, + 0x104B0, 0x104D3, + 0x10570, 0x1057A, + 0x1057C, 0x1058A, + 0x1058C, 0x10592, + 0x10594, 0x10595, + 0x10C80, 0x10CB2, + 0x118A0, 0x118BF, + 0x16E40, 0x16E5F, + 0x1D400, 0x1D419, + 0x1D434, 0x1D44D, + 0x1D468, 0x1D481, + 0x1D49C, 0x1D49C, + 0x1D49E, 0x1D49F, + 0x1D4A2, 0x1D4A2, + 0x1D4A5, 0x1D4A6, + 0x1D4A9, 0x1D4AC, + 0x1D4AE, 0x1D4B5, + 0x1D4D0, 0x1D4E9, + 0x1D504, 0x1D505, + 0x1D507, 0x1D50A, + 0x1D50D, 0x1D514, + 0x1D516, 0x1D51C, + 0x1D538, 0x1D539, + 0x1D53B, 0x1D53E, + 0x1D540, 0x1D544, + 0x1D546, 0x1D546, + 0x1D54A, 0x1D550, + 0x1D56C, 0x1D585, + 0x1D5A0, 0x1D5B9, + 0x1D5D4, 0x1D5ED, + 0x1D608, 0x1D621, + 0x1D63C, 0x1D655, + 0x1D670, 0x1D689, + 0x1D6A8, 0x1D6C0, + 0x1D6E2, 0x1D6FA, + 0x1D71C, 0x1D734, + 0x1D756, 0x1D76E, + 0x1D790, 0x1D7A8, + 0x1D7CA, 0x1D7CA, + 0x1E900, 0x1E921, + 0x1F130, 0x1F149, + 0x1F150, 0x1F169, + 0x1F170, 0x1F189, +}; + +static bool +unicode_codepoint_match(unicode_codepoint_t codepoint, unicode_codepoint_t *codepoints, size_t size) { + size_t start = 0; + size_t end = size; + + while (start < end) { + size_t middle = start + (end - start) / 2; + if ((middle % 2) != 0) middle--; + + if (codepoint >= codepoints[middle] && codepoint <= codepoints[middle + 1]) { + return true; + } + + if (codepoint < codepoints[middle]) { + end = middle; + } else { + start = middle + 2; + } + } + + return false; +} + +static const uint8_t utf_8_dfa[] = { + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 00..1f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 20..3f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 40..5f + 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, // 60..7f + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9,9, // 80..9f + 7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7,7, // a0..bf + 8,8,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, // c0..df + 0xa,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x3,0x4,0x3,0x3, // e0..ef + 0xb,0x6,0x6,0x6,0x5,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8,0x8, // f0..ff + 0x0,0x1,0x2,0x3,0x5,0x8,0x7,0x1,0x1,0x1,0x4,0x6,0x1,0x1,0x1,0x1, // s0..s0 + 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,0,1,1,1,1,1,0,1,0,1,1,1,1,1,1, // s1..s2 + 1,2,1,1,1,1,1,2,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1, // s3..s4 + 1,2,1,1,1,1,1,1,1,2,1,1,1,1,1,1,1,1,1,1,1,1,1,3,1,3,1,1,1,1,1,1, // s5..s6 + 1,3,1,1,1,1,1,3,1,3,1,1,1,1,1,1,1,3,1,1,1,1,1,1,1,1,1,1,1,1,1,1, // s7..s8 +}; + +static unicode_codepoint_t +utf_8_codepoint(const unsigned char *c, size_t *width) { + uint32_t codepoint; + uint32_t state = 0; + + for (size_t index = 0; index < 4; index++) { + uint32_t byte = c[index]; + uint32_t type = utf_8_dfa[byte]; + + codepoint = (state != 0) ? + (byte & 0x3fu) | (codepoint << 6) : + (0xffu >> type) & (byte); + + state = utf_8_dfa[256 + (state * 16) + type]; + if (!state) { + *width = index + 1; + return (unicode_codepoint_t) codepoint; + } + } + + *width = 0; + return 0; +} + +static size_t +yp_encoding_utf_8_char_width(const char *c) { + size_t width; + const unsigned char *v = (const unsigned char *) c; + + utf_8_codepoint(v, &width); + return width; +} + +size_t +yp_encoding_utf_8_alpha_char(const char *c) { + const unsigned char *v = (const unsigned char *) c; + if (*v < 0x80) { + return (yp_encoding_unicode_table[*v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; + } + + size_t width; + unicode_codepoint_t codepoint = utf_8_codepoint(v, &width); + + if (codepoint <= 0xFF) { + return (yp_encoding_unicode_table[(unsigned char) codepoint] & YP_ENCODING_ALPHABETIC_BIT) ? width : 0; + } else { + return unicode_codepoint_match(codepoint, unicode_alpha_codepoints, UNICODE_ALPHA_CODEPOINTS_LENGTH) ? width : 0; + } +} + +size_t +yp_encoding_utf_8_alnum_char(const char *c) { + const unsigned char *v = (const unsigned char *) c; + if (*v < 0x80) { + return (yp_encoding_unicode_table[*v] & (YP_ENCODING_ALPHANUMERIC_BIT)) ? 1 : 0; + } + + size_t width; + unicode_codepoint_t codepoint = utf_8_codepoint(v, &width); + + if (codepoint <= 0xFF) { + return (yp_encoding_unicode_table[(unsigned char) codepoint] & (YP_ENCODING_ALPHANUMERIC_BIT)) ? width : 0; + } else { + return unicode_codepoint_match(codepoint, unicode_alnum_codepoints, UNICODE_ALNUM_CODEPOINTS_LENGTH) ? width : 0; + } +} + +static bool +yp_encoding_utf_8_isupper_char(const char *c) { + const unsigned char *v = (const unsigned char *) c; + if (*v < 0x80) { + return (yp_encoding_unicode_table[*v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; + } + + size_t width; + unicode_codepoint_t codepoint = utf_8_codepoint(v, &width); + + if (codepoint <= 0xFF) { + return (yp_encoding_unicode_table[(unsigned char) codepoint] & YP_ENCODING_UPPERCASE_BIT) ? true : false; + } else { + return unicode_codepoint_match(codepoint, unicode_isupper_codepoints, UNICODE_ISUPPER_CODEPOINTS_LENGTH) ? true : false; + } +} + +#undef UNICODE_ALPHA_CODEPOINTS_LENGTH +#undef UNICODE_ALNUM_CODEPOINTS_LENGTH +#undef UNICODE_ISUPPER_CODEPOINTS_LENGTH + +yp_encoding_t yp_encoding_utf_8 = { + .name = "utf-8", + .char_width = yp_encoding_utf_8_char_width, + .alnum_char = yp_encoding_utf_8_alnum_char, + .alpha_char = yp_encoding_utf_8_alpha_char, + .isupper_char = yp_encoding_utf_8_isupper_char, + .multibyte = true +}; diff --git a/src/main/c/yarp/src/enc/windows_1251.c b/src/main/c/yarp/src/enc/windows_1251.c new file mode 100644 index 000000000000..37344e1ebf5b --- /dev/null +++ b/src/main/c/yarp/src/enc/windows_1251.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding windows-1251 character. +static unsigned char yp_encoding_windows_1251_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 7, 7, 0, 3, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7, 7, 7, // 8x + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 3, 3, 3, // 9x + 0, 7, 3, 7, 0, 7, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, // Ax + 0, 0, 7, 3, 3, 3, 0, 0, 3, 0, 3, 0, 3, 7, 3, 3, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +static size_t +yp_encoding_windows_1251_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_windows_1251_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_windows_1251_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_windows_1251_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_windows_1251_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_windows_1251_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_windows_1251 = { + .name = "windows-1251", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_windows_1251_alnum_char, + .alpha_char = yp_encoding_windows_1251_alpha_char, + .isupper_char = yp_encoding_windows_1251_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/windows_1252.c b/src/main/c/yarp/src/enc/windows_1252.c new file mode 100644 index 000000000000..a62d4f4a2102 --- /dev/null +++ b/src/main/c/yarp/src/enc/windows_1252.c @@ -0,0 +1,50 @@ +#include "yarp/enc/yp_encoding.h" + +// Each element of the following table contains a bitfield that indicates a +// piece of information about the corresponding windows-1252 character. +static unsigned char yp_encoding_windows_1252_table[256] = { +// 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x + 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x + 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 7, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 7, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx + 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx + 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx + 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex + 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx +}; + +static size_t +yp_encoding_windows_1252_alpha_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_windows_1252_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; +} + +static size_t +yp_encoding_windows_1252_alnum_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_windows_1252_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; +} + +static bool +yp_encoding_windows_1252_isupper_char(const char *c) { + const unsigned char v = (const unsigned char) *c; + return (yp_encoding_windows_1252_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; +} + +yp_encoding_t yp_encoding_windows_1252 = { + .name = "windows-1252", + .char_width = yp_encoding_single_char_width, + .alnum_char = yp_encoding_windows_1252_alnum_char, + .alpha_char = yp_encoding_windows_1252_alpha_char, + .isupper_char = yp_encoding_windows_1252_isupper_char, + .multibyte = false +}; diff --git a/src/main/c/yarp/src/enc/windows_31j.c b/src/main/c/yarp/src/enc/windows_31j.c new file mode 100644 index 000000000000..9aa62c9b101a --- /dev/null +++ b/src/main/c/yarp/src/enc/windows_31j.c @@ -0,0 +1,82 @@ +#include "yarp/enc/yp_encoding.h" + +typedef uint16_t windows_31j_codepoint_t; + +static windows_31j_codepoint_t +windows_31j_codepoint(const char *c, size_t *width) { + const unsigned char *uc = (const unsigned char *) c; + + // These are the single byte characters. + if (*uc < 0x80 || (*uc >= 0xA1 && *uc <= 0xDF)) { + *width = 1; + return *uc; + } + + // These are the double byte characters. + if ( + ((uc[0] >= 0x81 && uc[0] <= 0x9F) || (uc[0] >= 0xE0 && uc[0] <= 0xFC)) && + (uc[1] >= 0x40 && uc[1] <= 0xFC) + ) { + *width = 2; + return (windows_31j_codepoint_t) (uc[0] << 8 | uc[1]); + } + + *width = 0; + return 0; +} + +static size_t +yp_encoding_windows_31j_char_width(const char *c) { + size_t width; + windows_31j_codepoint(c, &width); + + return width; +} + +static size_t +yp_encoding_windows_31j_alpha_char(const char *c) { + size_t width; + windows_31j_codepoint_t codepoint = windows_31j_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_alpha_char(&value); + } else { + return 0; + } +} + +static size_t +yp_encoding_windows_31j_alnum_char(const char *c) { + size_t width; + windows_31j_codepoint_t codepoint = windows_31j_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_alnum_char(&value); + } else { + return 0; + } +} + +static bool +yp_encoding_windows_31j_isupper_char(const char *c) { + size_t width; + windows_31j_codepoint_t codepoint = windows_31j_codepoint(c, &width); + + if (width == 1) { + const char value = (const char) codepoint; + return yp_encoding_ascii_isupper_char(&value); + } else { + return false; + } +} + +yp_encoding_t yp_encoding_windows_31j = { + .name = "windows-31j", + .char_width = yp_encoding_windows_31j_char_width, + .alnum_char = yp_encoding_windows_31j_alnum_char, + .alpha_char = yp_encoding_windows_31j_alpha_char, + .isupper_char = yp_encoding_windows_31j_isupper_char, + .multibyte = true +}; diff --git a/src/main/c/yarp/src/node.c b/src/main/c/yarp/src/node.c new file mode 100644 index 000000000000..0b45624fc7d0 --- /dev/null +++ b/src/main/c/yarp/src/node.c @@ -0,0 +1,1734 @@ +/******************************************************************************/ +/* This file is generated by the bin/template script and should not be */ +/* modified manually. See */ +/* templates/src/node.c.erb */ +/* if you are looking to modify the */ +/* template */ +/******************************************************************************/ +#line 2 "node.c.erb" +#include "yarp/node.h" + +// Clear the node but preserves the location. +void yp_node_clear(yp_node_t *node) { + yp_location_t location = node->location; + memset(node, 0, sizeof(yp_node_t)); + node->location = location; +} + +// Calculate the size of the token list in bytes. +static size_t +yp_location_list_memsize(yp_location_list_t *list) { + return sizeof(yp_location_list_t) + (list->capacity * sizeof(yp_location_t)); +} + +// Append a token to the given list. +void +yp_location_list_append(yp_location_list_t *list, const yp_token_t *token) { + if (list->size == list->capacity) { + list->capacity = list->capacity == 0 ? 2 : list->capacity * 2; + list->locations = (yp_location_t *) realloc(list->locations, sizeof(yp_location_t) * list->capacity); + } + list->locations[list->size++] = (yp_location_t) { .start = token->start, .end = token->end }; +} + +// Free the memory associated with the token list. +static void +yp_location_list_free(yp_location_list_t *list) { + if (list->locations != NULL) { + free(list->locations); + } +} + +static void +yp_node_memsize_node(yp_node_t *node, yp_memsize_t *memsize); + +// Calculate the size of the node list in bytes. +static size_t +yp_node_list_memsize(yp_node_list_t *node_list, yp_memsize_t *memsize) { + size_t size = sizeof(yp_node_list_t) + (node_list->capacity * sizeof(yp_node_t *)); + for (size_t index = 0; index < node_list->size; index++) { + yp_node_memsize_node(node_list->nodes[index], memsize); + } + return size; +} + +// Append a new node onto the end of the node list. +void +yp_node_list_append(yp_node_list_t *list, yp_node_t *node) { + if (list->size == list->capacity) { + list->capacity = list->capacity == 0 ? 4 : list->capacity * 2; + list->nodes = (yp_node_t **) realloc(list->nodes, sizeof(yp_node_t *) * list->capacity); + } + list->nodes[list->size++] = node; +} + +YP_EXPORTED_FUNCTION void +yp_node_destroy(yp_parser_t *parser, yp_node_t *node); + +// Deallocate the inner memory of a list of nodes. The parser argument is not +// used, but is here for the future possibility of pre-allocating memory pools. +static void +yp_node_list_free(yp_parser_t *parser, yp_node_list_t *list) { + if (list->capacity > 0) { + for (size_t index = 0; index < list->size; index++) { + yp_node_destroy(parser, list->nodes[index]); + } + free(list->nodes); + } +} + +// Deallocate the space for a yp_node_t. Similarly to yp_node_alloc, we're not +// using the parser argument, but it's there to allow for the future possibility +// of pre-allocating larger memory pools. +YP_EXPORTED_FUNCTION void +yp_node_destroy(yp_parser_t *parser, yp_node_t *node) { + switch (node->type) { +#line 81 "node.c.erb" + case YP_NODE_ALIAS_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_alias_node_t *)node)->new_name); + yp_node_destroy(parser, (yp_node_t *)((yp_alias_node_t *)node)->old_name); + break; +#line 81 "node.c.erb" + case YP_NODE_ALTERNATION_PATTERN_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->left); + yp_node_destroy(parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->right); + break; +#line 81 "node.c.erb" + case YP_NODE_AND_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_and_node_t *)node)->left); + yp_node_destroy(parser, (yp_node_t *)((yp_and_node_t *)node)->right); + break; +#line 81 "node.c.erb" + case YP_NODE_ARGUMENTS_NODE: + yp_node_list_free(parser, &((yp_arguments_node_t *)node)->arguments); + break; +#line 81 "node.c.erb" + case YP_NODE_ARRAY_NODE: + yp_node_list_free(parser, &((yp_array_node_t *)node)->elements); + break; +#line 81 "node.c.erb" + case YP_NODE_ARRAY_PATTERN_NODE: + if (((yp_array_pattern_node_t *)node)->constant != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->constant); + } + yp_node_list_free(parser, &((yp_array_pattern_node_t *)node)->requireds); + if (((yp_array_pattern_node_t *)node)->rest != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->rest); + } + yp_node_list_free(parser, &((yp_array_pattern_node_t *)node)->posts); + break; +#line 81 "node.c.erb" + case YP_NODE_ASSOC_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_assoc_node_t *)node)->key); + if (((yp_assoc_node_t *)node)->value != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_assoc_node_t *)node)->value); + } + break; +#line 81 "node.c.erb" + case YP_NODE_ASSOC_SPLAT_NODE: + if (((yp_assoc_splat_node_t *)node)->value != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_assoc_splat_node_t *)node)->value); + } + break; +#line 81 "node.c.erb" + case YP_NODE_BACK_REFERENCE_READ_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_BEGIN_NODE: + if (((yp_begin_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_begin_node_t *)node)->statements); + } + if (((yp_begin_node_t *)node)->rescue_clause != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_begin_node_t *)node)->rescue_clause); + } + if (((yp_begin_node_t *)node)->else_clause != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_begin_node_t *)node)->else_clause); + } + if (((yp_begin_node_t *)node)->ensure_clause != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_begin_node_t *)node)->ensure_clause); + } + break; +#line 81 "node.c.erb" + case YP_NODE_BLOCK_ARGUMENT_NODE: + if (((yp_block_argument_node_t *)node)->expression != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_block_argument_node_t *)node)->expression); + } + break; +#line 81 "node.c.erb" + case YP_NODE_BLOCK_NODE: + yp_constant_id_list_free(&((yp_block_node_t *)node)->locals); + if (((yp_block_node_t *)node)->parameters != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_block_node_t *)node)->parameters); + } + if (((yp_block_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_block_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_BLOCK_PARAMETER_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_BLOCK_PARAMETERS_NODE: + if (((yp_block_parameters_node_t *)node)->parameters != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_block_parameters_node_t *)node)->parameters); + } + yp_location_list_free(&((yp_block_parameters_node_t *)node)->locals); + break; +#line 81 "node.c.erb" + case YP_NODE_BREAK_NODE: + if (((yp_break_node_t *)node)->arguments != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_break_node_t *)node)->arguments); + } + break; +#line 81 "node.c.erb" + case YP_NODE_CALL_NODE: + if (((yp_call_node_t *)node)->receiver != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_call_node_t *)node)->receiver); + } + if (((yp_call_node_t *)node)->arguments != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_call_node_t *)node)->arguments); + } + if (((yp_call_node_t *)node)->block != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_call_node_t *)node)->block); + } + yp_string_free(&((yp_call_node_t *)node)->name); + break; +#line 81 "node.c.erb" + case YP_NODE_CALL_OPERATOR_AND_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_call_operator_and_write_node_t *)node)->target); + yp_node_destroy(parser, (yp_node_t *)((yp_call_operator_and_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CALL_OPERATOR_OR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_call_operator_or_write_node_t *)node)->target); + yp_node_destroy(parser, (yp_node_t *)((yp_call_operator_or_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CALL_OPERATOR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_call_operator_write_node_t *)node)->target); + yp_node_destroy(parser, (yp_node_t *)((yp_call_operator_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CAPTURE_PATTERN_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->value); + yp_node_destroy(parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->target); + break; +#line 81 "node.c.erb" + case YP_NODE_CASE_NODE: + if (((yp_case_node_t *)node)->predicate != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_case_node_t *)node)->predicate); + } + yp_node_list_free(parser, &((yp_case_node_t *)node)->conditions); + if (((yp_case_node_t *)node)->consequent != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_case_node_t *)node)->consequent); + } + break; +#line 81 "node.c.erb" + case YP_NODE_CLASS_NODE: + yp_constant_id_list_free(&((yp_class_node_t *)node)->locals); + yp_node_destroy(parser, (yp_node_t *)((yp_class_node_t *)node)->constant_path); + if (((yp_class_node_t *)node)->superclass != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_class_node_t *)node)->superclass); + } + if (((yp_class_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_class_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_CLASS_VARIABLE_OPERATOR_AND_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_class_variable_operator_and_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CLASS_VARIABLE_OPERATOR_OR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_class_variable_operator_or_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CLASS_VARIABLE_OPERATOR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_class_variable_operator_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CLASS_VARIABLE_READ_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_CLASS_VARIABLE_WRITE_NODE: + if (((yp_class_variable_write_node_t *)node)->value != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_class_variable_write_node_t *)node)->value); + } + break; +#line 81 "node.c.erb" + case YP_NODE_CONSTANT_OPERATOR_AND_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_constant_operator_and_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CONSTANT_OPERATOR_OR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_constant_operator_or_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CONSTANT_OPERATOR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_constant_operator_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CONSTANT_PATH_NODE: + if (((yp_constant_path_node_t *)node)->parent != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_node_t *)node)->parent); + } + yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_node_t *)node)->child); + break; +#line 81 "node.c.erb" + case YP_NODE_CONSTANT_PATH_OPERATOR_AND_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_operator_and_write_node_t *)node)->target); + yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_operator_and_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CONSTANT_PATH_OPERATOR_OR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_operator_or_write_node_t *)node)->target); + yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_operator_or_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CONSTANT_PATH_OPERATOR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_operator_write_node_t *)node)->target); + yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_operator_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_CONSTANT_PATH_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->target); + if (((yp_constant_path_write_node_t *)node)->value != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->value); + } + break; +#line 81 "node.c.erb" + case YP_NODE_CONSTANT_READ_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_DEF_NODE: + if (((yp_def_node_t *)node)->receiver != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_def_node_t *)node)->receiver); + } + if (((yp_def_node_t *)node)->parameters != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_def_node_t *)node)->parameters); + } + if (((yp_def_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_def_node_t *)node)->statements); + } + yp_constant_id_list_free(&((yp_def_node_t *)node)->locals); + break; +#line 81 "node.c.erb" + case YP_NODE_DEFINED_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_defined_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_ELSE_NODE: + if (((yp_else_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_else_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_EMBEDDED_STATEMENTS_NODE: + if (((yp_embedded_statements_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_embedded_statements_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_EMBEDDED_VARIABLE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_embedded_variable_node_t *)node)->variable); + break; +#line 81 "node.c.erb" + case YP_NODE_ENSURE_NODE: + if (((yp_ensure_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_ensure_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_FALSE_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_FIND_PATTERN_NODE: + if (((yp_find_pattern_node_t *)node)->constant != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->constant); + } + yp_node_destroy(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->left); + yp_node_list_free(parser, &((yp_find_pattern_node_t *)node)->requireds); + yp_node_destroy(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->right); + break; +#line 81 "node.c.erb" + case YP_NODE_FLOAT_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_FOR_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_for_node_t *)node)->index); + yp_node_destroy(parser, (yp_node_t *)((yp_for_node_t *)node)->collection); + if (((yp_for_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_for_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_FORWARDING_ARGUMENTS_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_FORWARDING_PARAMETER_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_FORWARDING_SUPER_NODE: + if (((yp_forwarding_super_node_t *)node)->block != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_forwarding_super_node_t *)node)->block); + } + break; +#line 81 "node.c.erb" + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_AND_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_global_variable_operator_and_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_OR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_global_variable_operator_or_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_global_variable_operator_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_GLOBAL_VARIABLE_READ_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_GLOBAL_VARIABLE_WRITE_NODE: + if (((yp_global_variable_write_node_t *)node)->value != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_global_variable_write_node_t *)node)->value); + } + break; +#line 81 "node.c.erb" + case YP_NODE_HASH_NODE: + yp_node_list_free(parser, &((yp_hash_node_t *)node)->elements); + break; +#line 81 "node.c.erb" + case YP_NODE_HASH_PATTERN_NODE: + if (((yp_hash_pattern_node_t *)node)->constant != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->constant); + } + yp_node_list_free(parser, &((yp_hash_pattern_node_t *)node)->assocs); + if (((yp_hash_pattern_node_t *)node)->kwrest != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->kwrest); + } + break; +#line 81 "node.c.erb" + case YP_NODE_IF_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_if_node_t *)node)->predicate); + if (((yp_if_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_if_node_t *)node)->statements); + } + if (((yp_if_node_t *)node)->consequent != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_if_node_t *)node)->consequent); + } + break; +#line 81 "node.c.erb" + case YP_NODE_IMAGINARY_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_imaginary_node_t *)node)->numeric); + break; +#line 81 "node.c.erb" + case YP_NODE_IN_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_in_node_t *)node)->pattern); + if (((yp_in_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_in_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_AND_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_instance_variable_operator_and_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_OR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_instance_variable_operator_or_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_instance_variable_operator_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_INSTANCE_VARIABLE_READ_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_INSTANCE_VARIABLE_WRITE_NODE: + if (((yp_instance_variable_write_node_t *)node)->value != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_instance_variable_write_node_t *)node)->value); + } + break; +#line 81 "node.c.erb" + case YP_NODE_INTEGER_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE: + yp_node_list_free(parser, &((yp_interpolated_regular_expression_node_t *)node)->parts); + break; +#line 81 "node.c.erb" + case YP_NODE_INTERPOLATED_STRING_NODE: + yp_node_list_free(parser, &((yp_interpolated_string_node_t *)node)->parts); + break; +#line 81 "node.c.erb" + case YP_NODE_INTERPOLATED_SYMBOL_NODE: + yp_node_list_free(parser, &((yp_interpolated_symbol_node_t *)node)->parts); + break; +#line 81 "node.c.erb" + case YP_NODE_INTERPOLATED_X_STRING_NODE: + yp_node_list_free(parser, &((yp_interpolated_x_string_node_t *)node)->parts); + break; +#line 81 "node.c.erb" + case YP_NODE_KEYWORD_HASH_NODE: + yp_node_list_free(parser, &((yp_keyword_hash_node_t *)node)->elements); + break; +#line 81 "node.c.erb" + case YP_NODE_KEYWORD_PARAMETER_NODE: + if (((yp_keyword_parameter_node_t *)node)->value != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_keyword_parameter_node_t *)node)->value); + } + break; +#line 81 "node.c.erb" + case YP_NODE_KEYWORD_REST_PARAMETER_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_LAMBDA_NODE: + yp_constant_id_list_free(&((yp_lambda_node_t *)node)->locals); + if (((yp_lambda_node_t *)node)->parameters != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_lambda_node_t *)node)->parameters); + } + if (((yp_lambda_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_lambda_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_LOCAL_VARIABLE_OPERATOR_AND_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_local_variable_operator_and_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_LOCAL_VARIABLE_OPERATOR_OR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_local_variable_operator_or_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_local_variable_operator_write_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_LOCAL_VARIABLE_READ_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_LOCAL_VARIABLE_WRITE_NODE: + if (((yp_local_variable_write_node_t *)node)->value != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_local_variable_write_node_t *)node)->value); + } + break; +#line 81 "node.c.erb" + case YP_NODE_MATCH_PREDICATE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->value); + yp_node_destroy(parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->pattern); + break; +#line 81 "node.c.erb" + case YP_NODE_MATCH_REQUIRED_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_match_required_node_t *)node)->value); + yp_node_destroy(parser, (yp_node_t *)((yp_match_required_node_t *)node)->pattern); + break; +#line 81 "node.c.erb" + case YP_NODE_MISSING_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_MODULE_NODE: + yp_constant_id_list_free(&((yp_module_node_t *)node)->locals); + yp_node_destroy(parser, (yp_node_t *)((yp_module_node_t *)node)->constant_path); + if (((yp_module_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_module_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_MULTI_WRITE_NODE: + yp_node_list_free(parser, &((yp_multi_write_node_t *)node)->targets); + if (((yp_multi_write_node_t *)node)->value != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_multi_write_node_t *)node)->value); + } + break; +#line 81 "node.c.erb" + case YP_NODE_NEXT_NODE: + if (((yp_next_node_t *)node)->arguments != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_next_node_t *)node)->arguments); + } + break; +#line 81 "node.c.erb" + case YP_NODE_NIL_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_NO_KEYWORDS_PARAMETER_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_NUMBERED_REFERENCE_READ_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_OPTIONAL_PARAMETER_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_optional_parameter_node_t *)node)->value); + break; +#line 81 "node.c.erb" + case YP_NODE_OR_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_or_node_t *)node)->left); + yp_node_destroy(parser, (yp_node_t *)((yp_or_node_t *)node)->right); + break; +#line 81 "node.c.erb" + case YP_NODE_PARAMETERS_NODE: + yp_node_list_free(parser, &((yp_parameters_node_t *)node)->requireds); + yp_node_list_free(parser, &((yp_parameters_node_t *)node)->optionals); + yp_node_list_free(parser, &((yp_parameters_node_t *)node)->posts); + if (((yp_parameters_node_t *)node)->rest != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_parameters_node_t *)node)->rest); + } + yp_node_list_free(parser, &((yp_parameters_node_t *)node)->keywords); + if (((yp_parameters_node_t *)node)->keyword_rest != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_parameters_node_t *)node)->keyword_rest); + } + if (((yp_parameters_node_t *)node)->block != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_parameters_node_t *)node)->block); + } + break; +#line 81 "node.c.erb" + case YP_NODE_PARENTHESES_NODE: + if (((yp_parentheses_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_parentheses_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_PINNED_EXPRESSION_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_pinned_expression_node_t *)node)->expression); + break; +#line 81 "node.c.erb" + case YP_NODE_PINNED_VARIABLE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_pinned_variable_node_t *)node)->variable); + break; +#line 81 "node.c.erb" + case YP_NODE_POST_EXECUTION_NODE: + if (((yp_post_execution_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_post_execution_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_PRE_EXECUTION_NODE: + if (((yp_pre_execution_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_pre_execution_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_PROGRAM_NODE: + yp_constant_id_list_free(&((yp_program_node_t *)node)->locals); + yp_node_destroy(parser, (yp_node_t *)((yp_program_node_t *)node)->statements); + break; +#line 81 "node.c.erb" + case YP_NODE_RANGE_NODE: + if (((yp_range_node_t *)node)->left != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_range_node_t *)node)->left); + } + if (((yp_range_node_t *)node)->right != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_range_node_t *)node)->right); + } + break; +#line 81 "node.c.erb" + case YP_NODE_RATIONAL_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_rational_node_t *)node)->numeric); + break; +#line 81 "node.c.erb" + case YP_NODE_REDO_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_REGULAR_EXPRESSION_NODE: + yp_string_free(&((yp_regular_expression_node_t *)node)->unescaped); + break; +#line 81 "node.c.erb" + case YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE: + yp_node_list_free(parser, &((yp_required_destructured_parameter_node_t *)node)->parameters); + break; +#line 81 "node.c.erb" + case YP_NODE_REQUIRED_PARAMETER_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_RESCUE_MODIFIER_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->expression); + yp_node_destroy(parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->rescue_expression); + break; +#line 81 "node.c.erb" + case YP_NODE_RESCUE_NODE: + yp_node_list_free(parser, &((yp_rescue_node_t *)node)->exceptions); + if (((yp_rescue_node_t *)node)->exception != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_rescue_node_t *)node)->exception); + } + if (((yp_rescue_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_rescue_node_t *)node)->statements); + } + if (((yp_rescue_node_t *)node)->consequent != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_rescue_node_t *)node)->consequent); + } + break; +#line 81 "node.c.erb" + case YP_NODE_REST_PARAMETER_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_RETRY_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_RETURN_NODE: + if (((yp_return_node_t *)node)->arguments != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_return_node_t *)node)->arguments); + } + break; +#line 81 "node.c.erb" + case YP_NODE_SELF_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_SINGLETON_CLASS_NODE: + yp_constant_id_list_free(&((yp_singleton_class_node_t *)node)->locals); + yp_node_destroy(parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->expression); + if (((yp_singleton_class_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_SOURCE_ENCODING_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_SOURCE_FILE_NODE: + yp_string_free(&((yp_source_file_node_t *)node)->filepath); + break; +#line 81 "node.c.erb" + case YP_NODE_SOURCE_LINE_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_SPLAT_NODE: + if (((yp_splat_node_t *)node)->expression != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_splat_node_t *)node)->expression); + } + break; +#line 81 "node.c.erb" + case YP_NODE_STATEMENTS_NODE: + yp_node_list_free(parser, &((yp_statements_node_t *)node)->body); + break; +#line 81 "node.c.erb" + case YP_NODE_STRING_CONCAT_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_string_concat_node_t *)node)->left); + yp_node_destroy(parser, (yp_node_t *)((yp_string_concat_node_t *)node)->right); + break; +#line 81 "node.c.erb" + case YP_NODE_STRING_NODE: + yp_string_free(&((yp_string_node_t *)node)->unescaped); + break; +#line 81 "node.c.erb" + case YP_NODE_SUPER_NODE: + if (((yp_super_node_t *)node)->arguments != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_super_node_t *)node)->arguments); + } + if (((yp_super_node_t *)node)->block != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_super_node_t *)node)->block); + } + break; +#line 81 "node.c.erb" + case YP_NODE_SYMBOL_NODE: + yp_string_free(&((yp_symbol_node_t *)node)->unescaped); + break; +#line 81 "node.c.erb" + case YP_NODE_TRUE_NODE: + break; +#line 81 "node.c.erb" + case YP_NODE_UNDEF_NODE: + yp_node_list_free(parser, &((yp_undef_node_t *)node)->names); + break; +#line 81 "node.c.erb" + case YP_NODE_UNLESS_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_unless_node_t *)node)->predicate); + if (((yp_unless_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_unless_node_t *)node)->statements); + } + if (((yp_unless_node_t *)node)->consequent != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_unless_node_t *)node)->consequent); + } + break; +#line 81 "node.c.erb" + case YP_NODE_UNTIL_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_until_node_t *)node)->predicate); + if (((yp_until_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_until_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_WHEN_NODE: + yp_node_list_free(parser, &((yp_when_node_t *)node)->conditions); + if (((yp_when_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_when_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_WHILE_NODE: + yp_node_destroy(parser, (yp_node_t *)((yp_while_node_t *)node)->predicate); + if (((yp_while_node_t *)node)->statements != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_while_node_t *)node)->statements); + } + break; +#line 81 "node.c.erb" + case YP_NODE_X_STRING_NODE: + yp_string_free(&((yp_x_string_node_t *)node)->unescaped); + break; +#line 81 "node.c.erb" + case YP_NODE_YIELD_NODE: + if (((yp_yield_node_t *)node)->arguments != NULL) { + yp_node_destroy(parser, (yp_node_t *)((yp_yield_node_t *)node)->arguments); + } + break; +#line 106 "node.c.erb" + default: + assert(false && "unreachable"); + break; + } + free(node); +} + +static void +yp_node_memsize_node(yp_node_t *node, yp_memsize_t *memsize) { + memsize->node_count++; + + switch (node->type) { +#line 120 "node.c.erb" + case YP_NODE_ALIAS_NODE: { + memsize->memsize += sizeof(yp_alias_node_t); + yp_node_memsize_node((yp_node_t *)((yp_alias_node_t *)node)->new_name, memsize); + yp_node_memsize_node((yp_node_t *)((yp_alias_node_t *)node)->old_name, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_ALTERNATION_PATTERN_NODE: { + memsize->memsize += sizeof(yp_alternation_pattern_node_t); + yp_node_memsize_node((yp_node_t *)((yp_alternation_pattern_node_t *)node)->left, memsize); + yp_node_memsize_node((yp_node_t *)((yp_alternation_pattern_node_t *)node)->right, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_AND_NODE: { + memsize->memsize += sizeof(yp_and_node_t); + yp_node_memsize_node((yp_node_t *)((yp_and_node_t *)node)->left, memsize); + yp_node_memsize_node((yp_node_t *)((yp_and_node_t *)node)->right, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_ARGUMENTS_NODE: { + memsize->memsize += sizeof(yp_arguments_node_t); + yp_node_list_memsize(&((yp_arguments_node_t *)node)->arguments, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_ARRAY_NODE: { + memsize->memsize += sizeof(yp_array_node_t); + yp_node_list_memsize(&((yp_array_node_t *)node)->elements, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_ARRAY_PATTERN_NODE: { + memsize->memsize += sizeof(yp_array_pattern_node_t); + if (((yp_array_pattern_node_t *)node)->constant != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_array_pattern_node_t *)node)->constant, memsize); + } + yp_node_list_memsize(&((yp_array_pattern_node_t *)node)->requireds, memsize); + if (((yp_array_pattern_node_t *)node)->rest != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_array_pattern_node_t *)node)->rest, memsize); + } + yp_node_list_memsize(&((yp_array_pattern_node_t *)node)->posts, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_ASSOC_NODE: { + memsize->memsize += sizeof(yp_assoc_node_t); + yp_node_memsize_node((yp_node_t *)((yp_assoc_node_t *)node)->key, memsize); + if (((yp_assoc_node_t *)node)->value != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_assoc_node_t *)node)->value, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_ASSOC_SPLAT_NODE: { + memsize->memsize += sizeof(yp_assoc_splat_node_t); + if (((yp_assoc_splat_node_t *)node)->value != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_assoc_splat_node_t *)node)->value, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_BACK_REFERENCE_READ_NODE: { + memsize->memsize += sizeof(yp_back_reference_read_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_BEGIN_NODE: { + memsize->memsize += sizeof(yp_begin_node_t); + if (((yp_begin_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_begin_node_t *)node)->statements, memsize); + } + if (((yp_begin_node_t *)node)->rescue_clause != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_begin_node_t *)node)->rescue_clause, memsize); + } + if (((yp_begin_node_t *)node)->else_clause != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_begin_node_t *)node)->else_clause, memsize); + } + if (((yp_begin_node_t *)node)->ensure_clause != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_begin_node_t *)node)->ensure_clause, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_BLOCK_ARGUMENT_NODE: { + memsize->memsize += sizeof(yp_block_argument_node_t); + if (((yp_block_argument_node_t *)node)->expression != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_block_argument_node_t *)node)->expression, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_BLOCK_NODE: { + memsize->memsize += sizeof(yp_block_node_t); + memsize->memsize += yp_constant_id_list_memsize(&((yp_block_node_t *)node)->locals); + if (((yp_block_node_t *)node)->parameters != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_block_node_t *)node)->parameters, memsize); + } + if (((yp_block_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_block_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_BLOCK_PARAMETER_NODE: { + memsize->memsize += sizeof(yp_block_parameter_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_BLOCK_PARAMETERS_NODE: { + memsize->memsize += sizeof(yp_block_parameters_node_t); + if (((yp_block_parameters_node_t *)node)->parameters != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_block_parameters_node_t *)node)->parameters, memsize); + } + memsize->memsize += yp_location_list_memsize(&((yp_block_parameters_node_t *)node)->locals); + break; + } +#line 120 "node.c.erb" + case YP_NODE_BREAK_NODE: { + memsize->memsize += sizeof(yp_break_node_t); + if (((yp_break_node_t *)node)->arguments != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_break_node_t *)node)->arguments, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_CALL_NODE: { + memsize->memsize += sizeof(yp_call_node_t); + if (((yp_call_node_t *)node)->receiver != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_call_node_t *)node)->receiver, memsize); + } + if (((yp_call_node_t *)node)->arguments != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_call_node_t *)node)->arguments, memsize); + } + if (((yp_call_node_t *)node)->block != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_call_node_t *)node)->block, memsize); + } + memsize->memsize += yp_string_memsize(&((yp_call_node_t *)node)->name); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CALL_OPERATOR_AND_WRITE_NODE: { + memsize->memsize += sizeof(yp_call_operator_and_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_call_operator_and_write_node_t *)node)->target, memsize); + yp_node_memsize_node((yp_node_t *)((yp_call_operator_and_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CALL_OPERATOR_OR_WRITE_NODE: { + memsize->memsize += sizeof(yp_call_operator_or_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_call_operator_or_write_node_t *)node)->target, memsize); + yp_node_memsize_node((yp_node_t *)((yp_call_operator_or_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CALL_OPERATOR_WRITE_NODE: { + memsize->memsize += sizeof(yp_call_operator_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_call_operator_write_node_t *)node)->target, memsize); + yp_node_memsize_node((yp_node_t *)((yp_call_operator_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CAPTURE_PATTERN_NODE: { + memsize->memsize += sizeof(yp_capture_pattern_node_t); + yp_node_memsize_node((yp_node_t *)((yp_capture_pattern_node_t *)node)->value, memsize); + yp_node_memsize_node((yp_node_t *)((yp_capture_pattern_node_t *)node)->target, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CASE_NODE: { + memsize->memsize += sizeof(yp_case_node_t); + if (((yp_case_node_t *)node)->predicate != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_case_node_t *)node)->predicate, memsize); + } + yp_node_list_memsize(&((yp_case_node_t *)node)->conditions, memsize); + if (((yp_case_node_t *)node)->consequent != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_case_node_t *)node)->consequent, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_CLASS_NODE: { + memsize->memsize += sizeof(yp_class_node_t); + memsize->memsize += yp_constant_id_list_memsize(&((yp_class_node_t *)node)->locals); + yp_node_memsize_node((yp_node_t *)((yp_class_node_t *)node)->constant_path, memsize); + if (((yp_class_node_t *)node)->superclass != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_class_node_t *)node)->superclass, memsize); + } + if (((yp_class_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_class_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_CLASS_VARIABLE_OPERATOR_AND_WRITE_NODE: { + memsize->memsize += sizeof(yp_class_variable_operator_and_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_class_variable_operator_and_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CLASS_VARIABLE_OPERATOR_OR_WRITE_NODE: { + memsize->memsize += sizeof(yp_class_variable_operator_or_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_class_variable_operator_or_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + memsize->memsize += sizeof(yp_class_variable_operator_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_class_variable_operator_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CLASS_VARIABLE_READ_NODE: { + memsize->memsize += sizeof(yp_class_variable_read_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CLASS_VARIABLE_WRITE_NODE: { + memsize->memsize += sizeof(yp_class_variable_write_node_t); + if (((yp_class_variable_write_node_t *)node)->value != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_class_variable_write_node_t *)node)->value, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_CONSTANT_OPERATOR_AND_WRITE_NODE: { + memsize->memsize += sizeof(yp_constant_operator_and_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_constant_operator_and_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CONSTANT_OPERATOR_OR_WRITE_NODE: { + memsize->memsize += sizeof(yp_constant_operator_or_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_constant_operator_or_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CONSTANT_OPERATOR_WRITE_NODE: { + memsize->memsize += sizeof(yp_constant_operator_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_constant_operator_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CONSTANT_PATH_NODE: { + memsize->memsize += sizeof(yp_constant_path_node_t); + if (((yp_constant_path_node_t *)node)->parent != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_constant_path_node_t *)node)->parent, memsize); + } + yp_node_memsize_node((yp_node_t *)((yp_constant_path_node_t *)node)->child, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CONSTANT_PATH_OPERATOR_AND_WRITE_NODE: { + memsize->memsize += sizeof(yp_constant_path_operator_and_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_constant_path_operator_and_write_node_t *)node)->target, memsize); + yp_node_memsize_node((yp_node_t *)((yp_constant_path_operator_and_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CONSTANT_PATH_OPERATOR_OR_WRITE_NODE: { + memsize->memsize += sizeof(yp_constant_path_operator_or_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_constant_path_operator_or_write_node_t *)node)->target, memsize); + yp_node_memsize_node((yp_node_t *)((yp_constant_path_operator_or_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + memsize->memsize += sizeof(yp_constant_path_operator_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_constant_path_operator_write_node_t *)node)->target, memsize); + yp_node_memsize_node((yp_node_t *)((yp_constant_path_operator_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_CONSTANT_PATH_WRITE_NODE: { + memsize->memsize += sizeof(yp_constant_path_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_constant_path_write_node_t *)node)->target, memsize); + if (((yp_constant_path_write_node_t *)node)->value != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_constant_path_write_node_t *)node)->value, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_CONSTANT_READ_NODE: { + memsize->memsize += sizeof(yp_constant_read_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_DEF_NODE: { + memsize->memsize += sizeof(yp_def_node_t); + if (((yp_def_node_t *)node)->receiver != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_def_node_t *)node)->receiver, memsize); + } + if (((yp_def_node_t *)node)->parameters != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_def_node_t *)node)->parameters, memsize); + } + if (((yp_def_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_def_node_t *)node)->statements, memsize); + } + memsize->memsize += yp_constant_id_list_memsize(&((yp_def_node_t *)node)->locals); + break; + } +#line 120 "node.c.erb" + case YP_NODE_DEFINED_NODE: { + memsize->memsize += sizeof(yp_defined_node_t); + yp_node_memsize_node((yp_node_t *)((yp_defined_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_ELSE_NODE: { + memsize->memsize += sizeof(yp_else_node_t); + if (((yp_else_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_else_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_EMBEDDED_STATEMENTS_NODE: { + memsize->memsize += sizeof(yp_embedded_statements_node_t); + if (((yp_embedded_statements_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_embedded_statements_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_EMBEDDED_VARIABLE_NODE: { + memsize->memsize += sizeof(yp_embedded_variable_node_t); + yp_node_memsize_node((yp_node_t *)((yp_embedded_variable_node_t *)node)->variable, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_ENSURE_NODE: { + memsize->memsize += sizeof(yp_ensure_node_t); + if (((yp_ensure_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_ensure_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_FALSE_NODE: { + memsize->memsize += sizeof(yp_false_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_FIND_PATTERN_NODE: { + memsize->memsize += sizeof(yp_find_pattern_node_t); + if (((yp_find_pattern_node_t *)node)->constant != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_find_pattern_node_t *)node)->constant, memsize); + } + yp_node_memsize_node((yp_node_t *)((yp_find_pattern_node_t *)node)->left, memsize); + yp_node_list_memsize(&((yp_find_pattern_node_t *)node)->requireds, memsize); + yp_node_memsize_node((yp_node_t *)((yp_find_pattern_node_t *)node)->right, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_FLOAT_NODE: { + memsize->memsize += sizeof(yp_float_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_FOR_NODE: { + memsize->memsize += sizeof(yp_for_node_t); + yp_node_memsize_node((yp_node_t *)((yp_for_node_t *)node)->index, memsize); + yp_node_memsize_node((yp_node_t *)((yp_for_node_t *)node)->collection, memsize); + if (((yp_for_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_for_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_FORWARDING_ARGUMENTS_NODE: { + memsize->memsize += sizeof(yp_forwarding_arguments_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_FORWARDING_PARAMETER_NODE: { + memsize->memsize += sizeof(yp_forwarding_parameter_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_FORWARDING_SUPER_NODE: { + memsize->memsize += sizeof(yp_forwarding_super_node_t); + if (((yp_forwarding_super_node_t *)node)->block != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_forwarding_super_node_t *)node)->block, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_AND_WRITE_NODE: { + memsize->memsize += sizeof(yp_global_variable_operator_and_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_global_variable_operator_and_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_OR_WRITE_NODE: { + memsize->memsize += sizeof(yp_global_variable_operator_or_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_global_variable_operator_or_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + memsize->memsize += sizeof(yp_global_variable_operator_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_global_variable_operator_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { + memsize->memsize += sizeof(yp_global_variable_read_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_GLOBAL_VARIABLE_WRITE_NODE: { + memsize->memsize += sizeof(yp_global_variable_write_node_t); + if (((yp_global_variable_write_node_t *)node)->value != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_global_variable_write_node_t *)node)->value, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_HASH_NODE: { + memsize->memsize += sizeof(yp_hash_node_t); + yp_node_list_memsize(&((yp_hash_node_t *)node)->elements, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_HASH_PATTERN_NODE: { + memsize->memsize += sizeof(yp_hash_pattern_node_t); + if (((yp_hash_pattern_node_t *)node)->constant != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_hash_pattern_node_t *)node)->constant, memsize); + } + yp_node_list_memsize(&((yp_hash_pattern_node_t *)node)->assocs, memsize); + if (((yp_hash_pattern_node_t *)node)->kwrest != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_hash_pattern_node_t *)node)->kwrest, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_IF_NODE: { + memsize->memsize += sizeof(yp_if_node_t); + yp_node_memsize_node((yp_node_t *)((yp_if_node_t *)node)->predicate, memsize); + if (((yp_if_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_if_node_t *)node)->statements, memsize); + } + if (((yp_if_node_t *)node)->consequent != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_if_node_t *)node)->consequent, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_IMAGINARY_NODE: { + memsize->memsize += sizeof(yp_imaginary_node_t); + yp_node_memsize_node((yp_node_t *)((yp_imaginary_node_t *)node)->numeric, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_IN_NODE: { + memsize->memsize += sizeof(yp_in_node_t); + yp_node_memsize_node((yp_node_t *)((yp_in_node_t *)node)->pattern, memsize); + if (((yp_in_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_in_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_AND_WRITE_NODE: { + memsize->memsize += sizeof(yp_instance_variable_operator_and_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_instance_variable_operator_and_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_OR_WRITE_NODE: { + memsize->memsize += sizeof(yp_instance_variable_operator_or_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_instance_variable_operator_or_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + memsize->memsize += sizeof(yp_instance_variable_operator_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_instance_variable_operator_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { + memsize->memsize += sizeof(yp_instance_variable_read_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_INSTANCE_VARIABLE_WRITE_NODE: { + memsize->memsize += sizeof(yp_instance_variable_write_node_t); + if (((yp_instance_variable_write_node_t *)node)->value != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_instance_variable_write_node_t *)node)->value, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_INTEGER_NODE: { + memsize->memsize += sizeof(yp_integer_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + memsize->memsize += sizeof(yp_interpolated_regular_expression_node_t); + yp_node_list_memsize(&((yp_interpolated_regular_expression_node_t *)node)->parts, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_INTERPOLATED_STRING_NODE: { + memsize->memsize += sizeof(yp_interpolated_string_node_t); + yp_node_list_memsize(&((yp_interpolated_string_node_t *)node)->parts, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_INTERPOLATED_SYMBOL_NODE: { + memsize->memsize += sizeof(yp_interpolated_symbol_node_t); + yp_node_list_memsize(&((yp_interpolated_symbol_node_t *)node)->parts, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_INTERPOLATED_X_STRING_NODE: { + memsize->memsize += sizeof(yp_interpolated_x_string_node_t); + yp_node_list_memsize(&((yp_interpolated_x_string_node_t *)node)->parts, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_KEYWORD_HASH_NODE: { + memsize->memsize += sizeof(yp_keyword_hash_node_t); + yp_node_list_memsize(&((yp_keyword_hash_node_t *)node)->elements, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_KEYWORD_PARAMETER_NODE: { + memsize->memsize += sizeof(yp_keyword_parameter_node_t); + if (((yp_keyword_parameter_node_t *)node)->value != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_keyword_parameter_node_t *)node)->value, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_KEYWORD_REST_PARAMETER_NODE: { + memsize->memsize += sizeof(yp_keyword_rest_parameter_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_LAMBDA_NODE: { + memsize->memsize += sizeof(yp_lambda_node_t); + memsize->memsize += yp_constant_id_list_memsize(&((yp_lambda_node_t *)node)->locals); + if (((yp_lambda_node_t *)node)->parameters != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_lambda_node_t *)node)->parameters, memsize); + } + if (((yp_lambda_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_lambda_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_LOCAL_VARIABLE_OPERATOR_AND_WRITE_NODE: { + memsize->memsize += sizeof(yp_local_variable_operator_and_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_local_variable_operator_and_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_LOCAL_VARIABLE_OPERATOR_OR_WRITE_NODE: { + memsize->memsize += sizeof(yp_local_variable_operator_or_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_local_variable_operator_or_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + memsize->memsize += sizeof(yp_local_variable_operator_write_node_t); + yp_node_memsize_node((yp_node_t *)((yp_local_variable_operator_write_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_LOCAL_VARIABLE_READ_NODE: { + memsize->memsize += sizeof(yp_local_variable_read_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_LOCAL_VARIABLE_WRITE_NODE: { + memsize->memsize += sizeof(yp_local_variable_write_node_t); + if (((yp_local_variable_write_node_t *)node)->value != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_local_variable_write_node_t *)node)->value, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_MATCH_PREDICATE_NODE: { + memsize->memsize += sizeof(yp_match_predicate_node_t); + yp_node_memsize_node((yp_node_t *)((yp_match_predicate_node_t *)node)->value, memsize); + yp_node_memsize_node((yp_node_t *)((yp_match_predicate_node_t *)node)->pattern, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_MATCH_REQUIRED_NODE: { + memsize->memsize += sizeof(yp_match_required_node_t); + yp_node_memsize_node((yp_node_t *)((yp_match_required_node_t *)node)->value, memsize); + yp_node_memsize_node((yp_node_t *)((yp_match_required_node_t *)node)->pattern, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_MISSING_NODE: { + memsize->memsize += sizeof(yp_missing_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_MODULE_NODE: { + memsize->memsize += sizeof(yp_module_node_t); + memsize->memsize += yp_constant_id_list_memsize(&((yp_module_node_t *)node)->locals); + yp_node_memsize_node((yp_node_t *)((yp_module_node_t *)node)->constant_path, memsize); + if (((yp_module_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_module_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_MULTI_WRITE_NODE: { + memsize->memsize += sizeof(yp_multi_write_node_t); + yp_node_list_memsize(&((yp_multi_write_node_t *)node)->targets, memsize); + if (((yp_multi_write_node_t *)node)->value != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_multi_write_node_t *)node)->value, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_NEXT_NODE: { + memsize->memsize += sizeof(yp_next_node_t); + if (((yp_next_node_t *)node)->arguments != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_next_node_t *)node)->arguments, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_NIL_NODE: { + memsize->memsize += sizeof(yp_nil_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_NO_KEYWORDS_PARAMETER_NODE: { + memsize->memsize += sizeof(yp_no_keywords_parameter_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_NUMBERED_REFERENCE_READ_NODE: { + memsize->memsize += sizeof(yp_numbered_reference_read_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_OPTIONAL_PARAMETER_NODE: { + memsize->memsize += sizeof(yp_optional_parameter_node_t); + yp_node_memsize_node((yp_node_t *)((yp_optional_parameter_node_t *)node)->value, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_OR_NODE: { + memsize->memsize += sizeof(yp_or_node_t); + yp_node_memsize_node((yp_node_t *)((yp_or_node_t *)node)->left, memsize); + yp_node_memsize_node((yp_node_t *)((yp_or_node_t *)node)->right, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_PARAMETERS_NODE: { + memsize->memsize += sizeof(yp_parameters_node_t); + yp_node_list_memsize(&((yp_parameters_node_t *)node)->requireds, memsize); + yp_node_list_memsize(&((yp_parameters_node_t *)node)->optionals, memsize); + yp_node_list_memsize(&((yp_parameters_node_t *)node)->posts, memsize); + if (((yp_parameters_node_t *)node)->rest != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_parameters_node_t *)node)->rest, memsize); + } + yp_node_list_memsize(&((yp_parameters_node_t *)node)->keywords, memsize); + if (((yp_parameters_node_t *)node)->keyword_rest != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_parameters_node_t *)node)->keyword_rest, memsize); + } + if (((yp_parameters_node_t *)node)->block != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_parameters_node_t *)node)->block, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_PARENTHESES_NODE: { + memsize->memsize += sizeof(yp_parentheses_node_t); + if (((yp_parentheses_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_parentheses_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_PINNED_EXPRESSION_NODE: { + memsize->memsize += sizeof(yp_pinned_expression_node_t); + yp_node_memsize_node((yp_node_t *)((yp_pinned_expression_node_t *)node)->expression, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_PINNED_VARIABLE_NODE: { + memsize->memsize += sizeof(yp_pinned_variable_node_t); + yp_node_memsize_node((yp_node_t *)((yp_pinned_variable_node_t *)node)->variable, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_POST_EXECUTION_NODE: { + memsize->memsize += sizeof(yp_post_execution_node_t); + if (((yp_post_execution_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_post_execution_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_PRE_EXECUTION_NODE: { + memsize->memsize += sizeof(yp_pre_execution_node_t); + if (((yp_pre_execution_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_pre_execution_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_PROGRAM_NODE: { + memsize->memsize += sizeof(yp_program_node_t); + memsize->memsize += yp_constant_id_list_memsize(&((yp_program_node_t *)node)->locals); + yp_node_memsize_node((yp_node_t *)((yp_program_node_t *)node)->statements, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_RANGE_NODE: { + memsize->memsize += sizeof(yp_range_node_t); + if (((yp_range_node_t *)node)->left != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_range_node_t *)node)->left, memsize); + } + if (((yp_range_node_t *)node)->right != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_range_node_t *)node)->right, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_RATIONAL_NODE: { + memsize->memsize += sizeof(yp_rational_node_t); + yp_node_memsize_node((yp_node_t *)((yp_rational_node_t *)node)->numeric, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_REDO_NODE: { + memsize->memsize += sizeof(yp_redo_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_REGULAR_EXPRESSION_NODE: { + memsize->memsize += sizeof(yp_regular_expression_node_t); + memsize->memsize += yp_string_memsize(&((yp_regular_expression_node_t *)node)->unescaped); + break; + } +#line 120 "node.c.erb" + case YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE: { + memsize->memsize += sizeof(yp_required_destructured_parameter_node_t); + yp_node_list_memsize(&((yp_required_destructured_parameter_node_t *)node)->parameters, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_REQUIRED_PARAMETER_NODE: { + memsize->memsize += sizeof(yp_required_parameter_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_RESCUE_MODIFIER_NODE: { + memsize->memsize += sizeof(yp_rescue_modifier_node_t); + yp_node_memsize_node((yp_node_t *)((yp_rescue_modifier_node_t *)node)->expression, memsize); + yp_node_memsize_node((yp_node_t *)((yp_rescue_modifier_node_t *)node)->rescue_expression, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_RESCUE_NODE: { + memsize->memsize += sizeof(yp_rescue_node_t); + yp_node_list_memsize(&((yp_rescue_node_t *)node)->exceptions, memsize); + if (((yp_rescue_node_t *)node)->exception != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_rescue_node_t *)node)->exception, memsize); + } + if (((yp_rescue_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_rescue_node_t *)node)->statements, memsize); + } + if (((yp_rescue_node_t *)node)->consequent != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_rescue_node_t *)node)->consequent, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_REST_PARAMETER_NODE: { + memsize->memsize += sizeof(yp_rest_parameter_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_RETRY_NODE: { + memsize->memsize += sizeof(yp_retry_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_RETURN_NODE: { + memsize->memsize += sizeof(yp_return_node_t); + if (((yp_return_node_t *)node)->arguments != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_return_node_t *)node)->arguments, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_SELF_NODE: { + memsize->memsize += sizeof(yp_self_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_SINGLETON_CLASS_NODE: { + memsize->memsize += sizeof(yp_singleton_class_node_t); + memsize->memsize += yp_constant_id_list_memsize(&((yp_singleton_class_node_t *)node)->locals); + yp_node_memsize_node((yp_node_t *)((yp_singleton_class_node_t *)node)->expression, memsize); + if (((yp_singleton_class_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_singleton_class_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_SOURCE_ENCODING_NODE: { + memsize->memsize += sizeof(yp_source_encoding_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_SOURCE_FILE_NODE: { + memsize->memsize += sizeof(yp_source_file_node_t); + memsize->memsize += yp_string_memsize(&((yp_source_file_node_t *)node)->filepath); + break; + } +#line 120 "node.c.erb" + case YP_NODE_SOURCE_LINE_NODE: { + memsize->memsize += sizeof(yp_source_line_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_SPLAT_NODE: { + memsize->memsize += sizeof(yp_splat_node_t); + if (((yp_splat_node_t *)node)->expression != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_splat_node_t *)node)->expression, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_STATEMENTS_NODE: { + memsize->memsize += sizeof(yp_statements_node_t); + yp_node_list_memsize(&((yp_statements_node_t *)node)->body, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_STRING_CONCAT_NODE: { + memsize->memsize += sizeof(yp_string_concat_node_t); + yp_node_memsize_node((yp_node_t *)((yp_string_concat_node_t *)node)->left, memsize); + yp_node_memsize_node((yp_node_t *)((yp_string_concat_node_t *)node)->right, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_STRING_NODE: { + memsize->memsize += sizeof(yp_string_node_t); + memsize->memsize += yp_string_memsize(&((yp_string_node_t *)node)->unescaped); + break; + } +#line 120 "node.c.erb" + case YP_NODE_SUPER_NODE: { + memsize->memsize += sizeof(yp_super_node_t); + if (((yp_super_node_t *)node)->arguments != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_super_node_t *)node)->arguments, memsize); + } + if (((yp_super_node_t *)node)->block != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_super_node_t *)node)->block, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_SYMBOL_NODE: { + memsize->memsize += sizeof(yp_symbol_node_t); + memsize->memsize += yp_string_memsize(&((yp_symbol_node_t *)node)->unescaped); + break; + } +#line 120 "node.c.erb" + case YP_NODE_TRUE_NODE: { + memsize->memsize += sizeof(yp_true_node_t); + break; + } +#line 120 "node.c.erb" + case YP_NODE_UNDEF_NODE: { + memsize->memsize += sizeof(yp_undef_node_t); + yp_node_list_memsize(&((yp_undef_node_t *)node)->names, memsize); + break; + } +#line 120 "node.c.erb" + case YP_NODE_UNLESS_NODE: { + memsize->memsize += sizeof(yp_unless_node_t); + yp_node_memsize_node((yp_node_t *)((yp_unless_node_t *)node)->predicate, memsize); + if (((yp_unless_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_unless_node_t *)node)->statements, memsize); + } + if (((yp_unless_node_t *)node)->consequent != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_unless_node_t *)node)->consequent, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_UNTIL_NODE: { + memsize->memsize += sizeof(yp_until_node_t); + yp_node_memsize_node((yp_node_t *)((yp_until_node_t *)node)->predicate, memsize); + if (((yp_until_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_until_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_WHEN_NODE: { + memsize->memsize += sizeof(yp_when_node_t); + yp_node_list_memsize(&((yp_when_node_t *)node)->conditions, memsize); + if (((yp_when_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_when_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_WHILE_NODE: { + memsize->memsize += sizeof(yp_while_node_t); + yp_node_memsize_node((yp_node_t *)((yp_while_node_t *)node)->predicate, memsize); + if (((yp_while_node_t *)node)->statements != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_while_node_t *)node)->statements, memsize); + } + break; + } +#line 120 "node.c.erb" + case YP_NODE_X_STRING_NODE: { + memsize->memsize += sizeof(yp_x_string_node_t); + memsize->memsize += yp_string_memsize(&((yp_x_string_node_t *)node)->unescaped); + break; + } +#line 120 "node.c.erb" + case YP_NODE_YIELD_NODE: { + memsize->memsize += sizeof(yp_yield_node_t); + if (((yp_yield_node_t *)node)->arguments != NULL) { + yp_node_memsize_node((yp_node_t *)((yp_yield_node_t *)node)->arguments, memsize); + } + break; + } +#line 147 "node.c.erb" + } +} + +// Calculates the memory footprint of a given node. +YP_EXPORTED_FUNCTION void +yp_node_memsize(yp_node_t *node, yp_memsize_t *memsize) { + *memsize = (yp_memsize_t) { .memsize = 0, .node_count = 0 }; + yp_node_memsize_node(node, memsize); +} diff --git a/src/main/c/yarp/src/pack.c b/src/main/c/yarp/src/pack.c new file mode 100644 index 000000000000..48bba4ea4917 --- /dev/null +++ b/src/main/c/yarp/src/pack.c @@ -0,0 +1,493 @@ +#include "yarp/pack.h" + +#include +#include + +static uintmax_t +strtoumaxc(const char **format); + +YP_EXPORTED_FUNCTION yp_pack_result +yp_pack_parse(yp_pack_variant variant, const char **format, const char *format_end, + yp_pack_type *type, yp_pack_signed *signed_type, yp_pack_endian *endian, yp_pack_size *size, + yp_pack_length_type *length_type, uint64_t *length, yp_pack_encoding *encoding) { + + if (*encoding == YP_PACK_ENCODING_START) { + *encoding = YP_PACK_ENCODING_US_ASCII; + } + + if (*format == format_end) { + *type = YP_PACK_END; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + *length_type = YP_PACK_LENGTH_NA; + return YP_PACK_OK; + } + + *length_type = YP_PACK_LENGTH_FIXED; + *length = 1; + bool length_changed_allowed = true; + + char directive = **format; + (*format)++; + switch (directive) { + case ' ': + case '\t': + case '\n': + case '\v': + case '\f': + case '\r': + *type = YP_PACK_SPACE; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + *length_type = YP_PACK_LENGTH_NA; + *length = 0; + return YP_PACK_OK; + case '#': + while ((*format < format_end) && (**format != '\n')) { + (*format)++; + } + *type = YP_PACK_COMMENT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + *length_type = YP_PACK_LENGTH_NA; + *length = 0; + return YP_PACK_OK; + case 'C': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_AGNOSTIC_ENDIAN; + *size = YP_PACK_SIZE_8; + break; + case 'S': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_16; + break; + case 'L': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_32; + break; + case 'Q': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_64; + break; + case 'J': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_P; + break; + case 'c': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_AGNOSTIC_ENDIAN; + *size = YP_PACK_SIZE_8; + break; + case 's': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_16; + break; + case 'l': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_32; + break; + case 'q': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_64; + break; + case 'j': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_P; + break; + case 'I': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_INT; + break; + case 'i': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_SIGNED; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_INT; + break; + case 'n': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_BIG_ENDIAN; + *size = YP_PACK_SIZE_16; + length_changed_allowed = false; + break; + case 'N': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_BIG_ENDIAN; + *size = YP_PACK_SIZE_32; + length_changed_allowed = false; + break; + case 'v': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_LITTLE_ENDIAN; + *size = YP_PACK_SIZE_16; + length_changed_allowed = false; + break; + case 'V': + *type = YP_PACK_INTEGER; + *signed_type = YP_PACK_UNSIGNED; + *endian = YP_PACK_LITTLE_ENDIAN; + *size = YP_PACK_SIZE_32; + length_changed_allowed = false; + break; + case 'U': + *type = YP_PACK_UTF8; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'w': + *type = YP_PACK_BER; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'D': + case 'd': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_64; + break; + case 'F': + case 'f': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_NATIVE_ENDIAN; + *size = YP_PACK_SIZE_32; + break; + case 'E': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_LITTLE_ENDIAN; + *size = YP_PACK_SIZE_64; + break; + case 'e': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_LITTLE_ENDIAN; + *size = YP_PACK_SIZE_32; + break; + case 'G': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_BIG_ENDIAN; + *size = YP_PACK_SIZE_64; + break; + case 'g': + *type = YP_PACK_FLOAT; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_BIG_ENDIAN; + *size = YP_PACK_SIZE_32; + break; + case 'A': + *type = YP_PACK_STRING_SPACE_PADDED; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'a': + *type = YP_PACK_STRING_NULL_PADDED; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'Z': + *type = YP_PACK_STRING_NULL_TERMINATED; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'B': + *type = YP_PACK_STRING_MSB; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'b': + *type = YP_PACK_STRING_LSB; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'H': + *type = YP_PACK_STRING_HEX_HIGH; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'h': + *type = YP_PACK_STRING_HEX_LOW; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'u': + *type = YP_PACK_STRING_UU; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'M': + *type = YP_PACK_STRING_MIME; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'm': + *type = YP_PACK_STRING_BASE64; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'P': + *type = YP_PACK_STRING_FIXED; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'p': + *type = YP_PACK_STRING_POINTER; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case '@': + *type = YP_PACK_MOVE; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'X': + *type = YP_PACK_BACK; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case 'x': + *type = YP_PACK_NULL; + *signed_type = YP_PACK_SIGNED_NA; + *endian = YP_PACK_ENDIAN_NA; + *size = YP_PACK_SIZE_NA; + break; + case '%': + return YP_PACK_ERROR_UNSUPPORTED_DIRECTIVE; + default: + return YP_PACK_ERROR_UNKNOWN_DIRECTIVE; + } + + bool explicit_endian = false; + + while (*format < format_end) { + switch (**format) { + case '_': + case '!': + (*format)++; + if (*type != YP_PACK_INTEGER || !length_changed_allowed) { + return YP_PACK_ERROR_BANG_NOT_ALLOWED; + } + switch (*size) { + case YP_PACK_SIZE_SHORT: + case YP_PACK_SIZE_INT: + case YP_PACK_SIZE_LONG: + case YP_PACK_SIZE_LONG_LONG: + break; + case YP_PACK_SIZE_16: + *size = YP_PACK_SIZE_SHORT; + break; + case YP_PACK_SIZE_32: + *size = YP_PACK_SIZE_LONG; + break; + case YP_PACK_SIZE_64: + *size = YP_PACK_SIZE_LONG_LONG; + break; + case YP_PACK_SIZE_P: + break; + default: + return YP_PACK_ERROR_BANG_NOT_ALLOWED; + } + break; + case '<': + (*format)++; + if (explicit_endian) { + return YP_PACK_ERROR_DOUBLE_ENDIAN; + } + *endian = YP_PACK_LITTLE_ENDIAN; + explicit_endian = true; + break; + case '>': + (*format)++; + if (explicit_endian) { + return YP_PACK_ERROR_DOUBLE_ENDIAN; + } + *endian = YP_PACK_BIG_ENDIAN; + explicit_endian = true; + break; + default: + goto exit_modifier_loop; + } + } + +exit_modifier_loop: + + if (variant == YP_PACK_VARIANT_UNPACK && *type == YP_PACK_MOVE) { + *length = 0; + } + + if (*format < format_end) { + if (**format == '*') { + switch (*type) { + case YP_PACK_NULL: + case YP_PACK_BACK: + switch (variant) { + case YP_PACK_VARIANT_PACK: + *length_type = YP_PACK_LENGTH_FIXED; + break; + case YP_PACK_VARIANT_UNPACK: + *length_type = YP_PACK_LENGTH_MAX; + break; + } + *length = 0; + break; + + case YP_PACK_MOVE: + switch (variant) { + case YP_PACK_VARIANT_PACK: + *length_type = YP_PACK_LENGTH_FIXED; + break; + case YP_PACK_VARIANT_UNPACK: + *length_type = YP_PACK_LENGTH_RELATIVE; + break; + } + *length = 0; + break; + + case YP_PACK_STRING_UU: + *length_type = YP_PACK_LENGTH_FIXED; + *length = 0; + break; + + case YP_PACK_STRING_FIXED: + switch (variant) { + case YP_PACK_VARIANT_PACK: + *length_type = YP_PACK_LENGTH_FIXED; + *length = 1; + break; + case YP_PACK_VARIANT_UNPACK: + *length_type = YP_PACK_LENGTH_MAX; + *length = 0; + break; + } + break; + + case YP_PACK_STRING_MIME: + case YP_PACK_STRING_BASE64: + *length_type = YP_PACK_LENGTH_FIXED; + *length = 1; + break; + + default: + *length_type = YP_PACK_LENGTH_MAX; + *length = 0; + break; + } + + (*format)++; + } else if (**format >= '0' && **format <= '9') { + errno = 0; + *length_type = YP_PACK_LENGTH_FIXED; + #if UINTMAX_MAX < UINT64_MAX + #error "YARP's design assumes uintmax_t is at least as large as uint64_t" + #endif + uintmax_t length_max = strtoumaxc(format); + if (errno || length_max > UINT64_MAX) { + return YP_PACK_ERROR_LENGTH_TOO_BIG; + } + *length = (uint64_t) length_max; + } + } + + switch (*type) { + case YP_PACK_UTF8: + /* if encoding is US-ASCII, upgrade to UTF-8 */ + if (*encoding == YP_PACK_ENCODING_US_ASCII) { + *encoding = YP_PACK_ENCODING_UTF_8; + } + break; + case YP_PACK_STRING_MIME: + case YP_PACK_STRING_BASE64: + case YP_PACK_STRING_UU: + /* keep US-ASCII (do nothing) */ + break; + default: + /* fall back to BINARY */ + *encoding = YP_PACK_ENCODING_ASCII_8BIT; + break; + } + + return YP_PACK_OK; +} + +YP_EXPORTED_FUNCTION size_t +yp_size_to_native(yp_pack_size size) { + switch (size) { + case YP_PACK_SIZE_SHORT: + return sizeof(short); + case YP_PACK_SIZE_INT: + return sizeof(int); + case YP_PACK_SIZE_LONG: + return sizeof(long); + case YP_PACK_SIZE_LONG_LONG: + return sizeof(long long); + case YP_PACK_SIZE_8: + return 1; + case YP_PACK_SIZE_16: + return 2; + case YP_PACK_SIZE_32: + return 4; + case YP_PACK_SIZE_64: + return 8; + case YP_PACK_SIZE_P: + return sizeof(void *); + default: + return 0; + } +} + +static uintmax_t +strtoumaxc(const char **format) { + uintmax_t value = 0; + while (**format >= '0' && **format <= '9') { + if (value > UINTMAX_MAX / 10) { + errno = ERANGE; + } + value = value * 10 + ((uintmax_t) (**format - '0')); + (*format)++; + } + return value; +} diff --git a/src/main/c/yarp/src/prettyprint.c b/src/main/c/yarp/src/prettyprint.c new file mode 100644 index 000000000000..f6350de298b5 --- /dev/null +++ b/src/main/c/yarp/src/prettyprint.c @@ -0,0 +1,1698 @@ +/******************************************************************************/ +/* This file is generated by the bin/template script and should not be */ +/* modified manually. See */ +/* templates/src/prettyprint.c.erb */ +/* if you are looking to modify the */ +/* template */ +/******************************************************************************/ +#include "yarp/defines.h" + +#include + +#include "yarp/ast.h" +#include "yarp/parser.h" +#include "yarp/util/yp_buffer.h" + +static void +prettyprint_location(yp_buffer_t *buffer, yp_parser_t *parser, yp_location_t *location) { + char printed[] = "[0000-0000]"; + snprintf(printed, sizeof(printed), "[%04ld-%04ld]", (long int)(location->start - parser->start), (long int)(location->end - parser->start)); + yp_buffer_append_str(buffer, printed, strlen(printed)); +} + +static void +prettyprint_node(yp_buffer_t *buffer, yp_parser_t *parser, yp_node_t *node) { + switch (node->type) { + case YP_NODE_ALIAS_NODE: { + yp_buffer_append_str(buffer, "AliasNode(", 10); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_alias_node_t *)node)->new_name); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_alias_node_t *)node)->old_name); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_alias_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_ALTERNATION_PATTERN_NODE: { + yp_buffer_append_str(buffer, "AlternationPatternNode(", 23); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->left); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->right); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_alternation_pattern_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_AND_NODE: { + yp_buffer_append_str(buffer, "AndNode(", 8); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_and_node_t *)node)->left); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_and_node_t *)node)->right); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_and_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_ARGUMENTS_NODE: { + yp_buffer_append_str(buffer, "ArgumentsNode(", 14); + for (uint32_t index = 0; index < ((yp_arguments_node_t *)node)->arguments.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_arguments_node_t *) node)->arguments.nodes[index]); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_ARRAY_NODE: { + yp_buffer_append_str(buffer, "ArrayNode(", 10); + for (uint32_t index = 0; index < ((yp_array_node_t *)node)->elements.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_array_node_t *) node)->elements.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_array_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_array_node_t *)node)->opening_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_array_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_array_node_t *)node)->closing_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_ARRAY_PATTERN_NODE: { + yp_buffer_append_str(buffer, "ArrayPatternNode(", 17); + if (((yp_array_pattern_node_t *)node)->constant == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->constant); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_array_pattern_node_t *)node)->requireds.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_array_pattern_node_t *) node)->requireds.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_array_pattern_node_t *)node)->rest == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->rest); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_array_pattern_node_t *)node)->posts.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_array_pattern_node_t *) node)->posts.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_array_pattern_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_array_pattern_node_t *)node)->opening_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_array_pattern_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_array_pattern_node_t *)node)->closing_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_ASSOC_NODE: { + yp_buffer_append_str(buffer, "AssocNode(", 10); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_assoc_node_t *)node)->key); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_assoc_node_t *)node)->value == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_assoc_node_t *)node)->value); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_assoc_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_assoc_node_t *)node)->operator_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_ASSOC_SPLAT_NODE: { + yp_buffer_append_str(buffer, "AssocSplatNode(", 15); + if (((yp_assoc_splat_node_t *)node)->value == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_assoc_splat_node_t *)node)->value); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_assoc_splat_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_BACK_REFERENCE_READ_NODE: { + yp_buffer_append_str(buffer, "BackReferenceReadNode(", 22); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_BEGIN_NODE: { + yp_buffer_append_str(buffer, "BeginNode(", 10); + if (((yp_begin_node_t *)node)->begin_keyword_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_begin_node_t *)node)->begin_keyword_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_begin_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_begin_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_begin_node_t *)node)->rescue_clause == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_begin_node_t *)node)->rescue_clause); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_begin_node_t *)node)->else_clause == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_begin_node_t *)node)->else_clause); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_begin_node_t *)node)->ensure_clause == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_begin_node_t *)node)->ensure_clause); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_begin_node_t *)node)->end_keyword_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_begin_node_t *)node)->end_keyword_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_BLOCK_ARGUMENT_NODE: { + yp_buffer_append_str(buffer, "BlockArgumentNode(", 18); + if (((yp_block_argument_node_t *)node)->expression == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_block_argument_node_t *)node)->expression); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_block_argument_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_BLOCK_NODE: { + yp_buffer_append_str(buffer, "BlockNode(", 10); + for (uint32_t index = 0; index < ((yp_block_node_t *)node)->locals.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + char locals_buffer[12]; + snprintf(locals_buffer, sizeof(locals_buffer), "%u", ((yp_block_node_t *)node)->locals.ids[index]); + yp_buffer_append_str(buffer, locals_buffer, strlen(locals_buffer)); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_block_node_t *)node)->parameters == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_block_node_t *)node)->parameters); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_block_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_block_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_block_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_block_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_BLOCK_PARAMETER_NODE: { + yp_buffer_append_str(buffer, "BlockParameterNode(", 19); + if (((yp_block_parameter_node_t *)node)->name_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_block_parameter_node_t *)node)->name_loc); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_block_parameter_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_BLOCK_PARAMETERS_NODE: { + yp_buffer_append_str(buffer, "BlockParametersNode(", 20); + if (((yp_block_parameters_node_t *)node)->parameters == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_block_parameters_node_t *)node)->parameters); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_block_parameters_node_t *)node)->locals.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_location(buffer, parser, &((yp_block_parameters_node_t *)node)->locals.locations[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_block_parameters_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_block_parameters_node_t *)node)->opening_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_block_parameters_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_block_parameters_node_t *)node)->closing_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_BREAK_NODE: { + yp_buffer_append_str(buffer, "BreakNode(", 10); + if (((yp_break_node_t *)node)->arguments == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_break_node_t *)node)->arguments); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_break_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CALL_NODE: { + yp_buffer_append_str(buffer, "CallNode(", 9); + if (((yp_call_node_t *)node)->receiver == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_node_t *)node)->receiver); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_call_node_t *)node)->operator_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->message_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_call_node_t *)node)->message_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_call_node_t *)node)->opening_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->arguments == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_node_t *)node)->arguments); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_call_node_t *)node)->closing_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->block == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_node_t *)node)->block); + } + yp_buffer_append_str(buffer, ", ", 2); char flags_buffer[12]; + snprintf(flags_buffer, sizeof(flags_buffer), "+%d", ((yp_call_node_t *)node)->flags); + yp_buffer_append_str(buffer, flags_buffer, strlen(flags_buffer)); + yp_buffer_append_str(buffer, ", ", 2); yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, yp_string_source(&((yp_call_node_t *)node)->name), yp_string_length(&((yp_call_node_t *)node)->name)); + yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CALL_OPERATOR_AND_WRITE_NODE: { + yp_buffer_append_str(buffer, "CallOperatorAndWriteNode(", 25); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_operator_and_write_node_t *)node)->target); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_call_operator_and_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_operator_and_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CALL_OPERATOR_OR_WRITE_NODE: { + yp_buffer_append_str(buffer, "CallOperatorOrWriteNode(", 24); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_operator_or_write_node_t *)node)->target); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_operator_or_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_call_operator_or_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CALL_OPERATOR_WRITE_NODE: { + yp_buffer_append_str(buffer, "CallOperatorWriteNode(", 22); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_operator_write_node_t *)node)->target); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_call_operator_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_operator_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); char operator_id_buffer[12]; + snprintf(operator_id_buffer, sizeof(operator_id_buffer), "%u", ((yp_call_operator_write_node_t *)node)->operator_id); + yp_buffer_append_str(buffer, operator_id_buffer, strlen(operator_id_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CAPTURE_PATTERN_NODE: { + yp_buffer_append_str(buffer, "CapturePatternNode(", 19); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->target); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_capture_pattern_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CASE_NODE: { + yp_buffer_append_str(buffer, "CaseNode(", 9); + if (((yp_case_node_t *)node)->predicate == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_case_node_t *)node)->predicate); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_case_node_t *)node)->conditions.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_case_node_t *) node)->conditions.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_case_node_t *)node)->consequent == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_case_node_t *)node)->consequent); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_case_node_t *)node)->case_keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_case_node_t *)node)->end_keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CLASS_NODE: { + yp_buffer_append_str(buffer, "ClassNode(", 10); + for (uint32_t index = 0; index < ((yp_class_node_t *)node)->locals.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + char locals_buffer[12]; + snprintf(locals_buffer, sizeof(locals_buffer), "%u", ((yp_class_node_t *)node)->locals.ids[index]); + yp_buffer_append_str(buffer, locals_buffer, strlen(locals_buffer)); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_class_node_t *)node)->class_keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_node_t *)node)->constant_path); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_class_node_t *)node)->inheritance_operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_class_node_t *)node)->inheritance_operator_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_class_node_t *)node)->superclass == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_node_t *)node)->superclass); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_class_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_class_node_t *)node)->end_keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CLASS_VARIABLE_OPERATOR_AND_WRITE_NODE: { + yp_buffer_append_str(buffer, "ClassVariableOperatorAndWriteNode(", 34); + prettyprint_location(buffer, parser, &((yp_class_variable_operator_and_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_class_variable_operator_and_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_variable_operator_and_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CLASS_VARIABLE_OPERATOR_OR_WRITE_NODE: { + yp_buffer_append_str(buffer, "ClassVariableOperatorOrWriteNode(", 33); + prettyprint_location(buffer, parser, &((yp_class_variable_operator_or_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_class_variable_operator_or_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_variable_operator_or_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + yp_buffer_append_str(buffer, "ClassVariableOperatorWriteNode(", 31); + prettyprint_location(buffer, parser, &((yp_class_variable_operator_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_class_variable_operator_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_variable_operator_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); char operator_buffer[12]; + snprintf(operator_buffer, sizeof(operator_buffer), "%u", ((yp_class_variable_operator_write_node_t *)node)->operator); + yp_buffer_append_str(buffer, operator_buffer, strlen(operator_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CLASS_VARIABLE_READ_NODE: { + yp_buffer_append_str(buffer, "ClassVariableReadNode(", 22); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CLASS_VARIABLE_WRITE_NODE: { + yp_buffer_append_str(buffer, "ClassVariableWriteNode(", 23); + prettyprint_location(buffer, parser, &((yp_class_variable_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_class_variable_write_node_t *)node)->value == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_variable_write_node_t *)node)->value); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_class_variable_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_class_variable_write_node_t *)node)->operator_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CONSTANT_OPERATOR_AND_WRITE_NODE: { + yp_buffer_append_str(buffer, "ConstantOperatorAndWriteNode(", 29); + prettyprint_location(buffer, parser, &((yp_constant_operator_and_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_constant_operator_and_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_operator_and_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CONSTANT_OPERATOR_OR_WRITE_NODE: { + yp_buffer_append_str(buffer, "ConstantOperatorOrWriteNode(", 28); + prettyprint_location(buffer, parser, &((yp_constant_operator_or_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_constant_operator_or_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_operator_or_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CONSTANT_OPERATOR_WRITE_NODE: { + yp_buffer_append_str(buffer, "ConstantOperatorWriteNode(", 26); + prettyprint_location(buffer, parser, &((yp_constant_operator_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_constant_operator_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_operator_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); char operator_buffer[12]; + snprintf(operator_buffer, sizeof(operator_buffer), "%u", ((yp_constant_operator_write_node_t *)node)->operator); + yp_buffer_append_str(buffer, operator_buffer, strlen(operator_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CONSTANT_PATH_NODE: { + yp_buffer_append_str(buffer, "ConstantPathNode(", 17); + if (((yp_constant_path_node_t *)node)->parent == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_node_t *)node)->parent); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_node_t *)node)->child); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_constant_path_node_t *)node)->delimiter_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CONSTANT_PATH_OPERATOR_AND_WRITE_NODE: { + yp_buffer_append_str(buffer, "ConstantPathOperatorAndWriteNode(", 33); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_operator_and_write_node_t *)node)->target); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_constant_path_operator_and_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_operator_and_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CONSTANT_PATH_OPERATOR_OR_WRITE_NODE: { + yp_buffer_append_str(buffer, "ConstantPathOperatorOrWriteNode(", 32); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_operator_or_write_node_t *)node)->target); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_constant_path_operator_or_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_operator_or_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + yp_buffer_append_str(buffer, "ConstantPathOperatorWriteNode(", 30); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_operator_write_node_t *)node)->target); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_constant_path_operator_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_operator_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); char operator_buffer[12]; + snprintf(operator_buffer, sizeof(operator_buffer), "%u", ((yp_constant_path_operator_write_node_t *)node)->operator); + yp_buffer_append_str(buffer, operator_buffer, strlen(operator_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CONSTANT_PATH_WRITE_NODE: { + yp_buffer_append_str(buffer, "ConstantPathWriteNode(", 22); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->target); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_constant_path_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_constant_path_write_node_t *)node)->operator_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_constant_path_write_node_t *)node)->value == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->value); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_CONSTANT_READ_NODE: { + yp_buffer_append_str(buffer, "ConstantReadNode(", 17); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_DEF_NODE: { + yp_buffer_append_str(buffer, "DefNode(", 8); + prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->receiver == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_def_node_t *)node)->receiver); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->parameters == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_def_node_t *)node)->parameters); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_def_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_def_node_t *)node)->locals.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + char locals_buffer[12]; + snprintf(locals_buffer, sizeof(locals_buffer), "%u", ((yp_def_node_t *)node)->locals.ids[index]); + yp_buffer_append_str(buffer, locals_buffer, strlen(locals_buffer)); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->def_keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->operator_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->lparen_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->lparen_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->rparen_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->rparen_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->equal_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->equal_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->end_keyword_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->end_keyword_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_DEFINED_NODE: { + yp_buffer_append_str(buffer, "DefinedNode(", 12); + if (((yp_defined_node_t *)node)->lparen_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_defined_node_t *)node)->lparen_loc); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_defined_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_defined_node_t *)node)->rparen_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_defined_node_t *)node)->rparen_loc); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_defined_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_ELSE_NODE: { + yp_buffer_append_str(buffer, "ElseNode(", 9); + prettyprint_location(buffer, parser, &((yp_else_node_t *)node)->else_keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_else_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_else_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_else_node_t *)node)->end_keyword_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_else_node_t *)node)->end_keyword_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_EMBEDDED_STATEMENTS_NODE: { + yp_buffer_append_str(buffer, "EmbeddedStatementsNode(", 23); + prettyprint_location(buffer, parser, &((yp_embedded_statements_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_embedded_statements_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_embedded_statements_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_embedded_statements_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_EMBEDDED_VARIABLE_NODE: { + yp_buffer_append_str(buffer, "EmbeddedVariableNode(", 21); + prettyprint_location(buffer, parser, &((yp_embedded_variable_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_embedded_variable_node_t *)node)->variable); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_ENSURE_NODE: { + yp_buffer_append_str(buffer, "EnsureNode(", 11); + prettyprint_location(buffer, parser, &((yp_ensure_node_t *)node)->ensure_keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_ensure_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_ensure_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_ensure_node_t *)node)->end_keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_FALSE_NODE: { + yp_buffer_append_str(buffer, "FalseNode(", 10); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_FIND_PATTERN_NODE: { + yp_buffer_append_str(buffer, "FindPatternNode(", 16); + if (((yp_find_pattern_node_t *)node)->constant == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->constant); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->left); + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_find_pattern_node_t *)node)->requireds.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_find_pattern_node_t *) node)->requireds.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->right); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_find_pattern_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_find_pattern_node_t *)node)->opening_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_find_pattern_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_find_pattern_node_t *)node)->closing_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_FLOAT_NODE: { + yp_buffer_append_str(buffer, "FloatNode(", 10); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_FOR_NODE: { + yp_buffer_append_str(buffer, "ForNode(", 8); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_for_node_t *)node)->index); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_for_node_t *)node)->collection); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_for_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_for_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_for_node_t *)node)->for_keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_for_node_t *)node)->in_keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_for_node_t *)node)->do_keyword_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_for_node_t *)node)->do_keyword_loc); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_for_node_t *)node)->end_keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_FORWARDING_ARGUMENTS_NODE: { + yp_buffer_append_str(buffer, "ForwardingArgumentsNode(", 24); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_FORWARDING_PARAMETER_NODE: { + yp_buffer_append_str(buffer, "ForwardingParameterNode(", 24); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_FORWARDING_SUPER_NODE: { + yp_buffer_append_str(buffer, "ForwardingSuperNode(", 20); + if (((yp_forwarding_super_node_t *)node)->block == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_forwarding_super_node_t *)node)->block); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_AND_WRITE_NODE: { + yp_buffer_append_str(buffer, "GlobalVariableOperatorAndWriteNode(", 35); + prettyprint_location(buffer, parser, &((yp_global_variable_operator_and_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_global_variable_operator_and_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_global_variable_operator_and_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_OR_WRITE_NODE: { + yp_buffer_append_str(buffer, "GlobalVariableOperatorOrWriteNode(", 34); + prettyprint_location(buffer, parser, &((yp_global_variable_operator_or_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_global_variable_operator_or_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_global_variable_operator_or_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + yp_buffer_append_str(buffer, "GlobalVariableOperatorWriteNode(", 32); + prettyprint_location(buffer, parser, &((yp_global_variable_operator_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_global_variable_operator_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_global_variable_operator_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); char operator_buffer[12]; + snprintf(operator_buffer, sizeof(operator_buffer), "%u", ((yp_global_variable_operator_write_node_t *)node)->operator); + yp_buffer_append_str(buffer, operator_buffer, strlen(operator_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { + yp_buffer_append_str(buffer, "GlobalVariableReadNode(", 23); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_GLOBAL_VARIABLE_WRITE_NODE: { + yp_buffer_append_str(buffer, "GlobalVariableWriteNode(", 24); + prettyprint_location(buffer, parser, &((yp_global_variable_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_global_variable_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_global_variable_write_node_t *)node)->operator_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_global_variable_write_node_t *)node)->value == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_global_variable_write_node_t *)node)->value); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_HASH_NODE: { + yp_buffer_append_str(buffer, "HashNode(", 9); + prettyprint_location(buffer, parser, &((yp_hash_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_hash_node_t *)node)->elements.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_hash_node_t *) node)->elements.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_hash_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_HASH_PATTERN_NODE: { + yp_buffer_append_str(buffer, "HashPatternNode(", 16); + if (((yp_hash_pattern_node_t *)node)->constant == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->constant); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_hash_pattern_node_t *)node)->assocs.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_hash_pattern_node_t *) node)->assocs.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_hash_pattern_node_t *)node)->kwrest == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->kwrest); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_hash_pattern_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_hash_pattern_node_t *)node)->opening_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_hash_pattern_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_hash_pattern_node_t *)node)->closing_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_IF_NODE: { + yp_buffer_append_str(buffer, "IfNode(", 7); + if (((yp_if_node_t *)node)->if_keyword_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_if_node_t *)node)->if_keyword_loc); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_if_node_t *)node)->predicate); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_if_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_if_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_if_node_t *)node)->consequent == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_if_node_t *)node)->consequent); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_if_node_t *)node)->end_keyword_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_if_node_t *)node)->end_keyword_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_IMAGINARY_NODE: { + yp_buffer_append_str(buffer, "ImaginaryNode(", 14); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_imaginary_node_t *)node)->numeric); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_IN_NODE: { + yp_buffer_append_str(buffer, "InNode(", 7); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_in_node_t *)node)->pattern); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_in_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_in_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_in_node_t *)node)->in_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_in_node_t *)node)->then_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_in_node_t *)node)->then_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_AND_WRITE_NODE: { + yp_buffer_append_str(buffer, "InstanceVariableOperatorAndWriteNode(", 37); + prettyprint_location(buffer, parser, &((yp_instance_variable_operator_and_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_instance_variable_operator_and_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_instance_variable_operator_and_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_OR_WRITE_NODE: { + yp_buffer_append_str(buffer, "InstanceVariableOperatorOrWriteNode(", 36); + prettyprint_location(buffer, parser, &((yp_instance_variable_operator_or_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_instance_variable_operator_or_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_instance_variable_operator_or_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + yp_buffer_append_str(buffer, "InstanceVariableOperatorWriteNode(", 34); + prettyprint_location(buffer, parser, &((yp_instance_variable_operator_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_instance_variable_operator_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_instance_variable_operator_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); char operator_buffer[12]; + snprintf(operator_buffer, sizeof(operator_buffer), "%u", ((yp_instance_variable_operator_write_node_t *)node)->operator); + yp_buffer_append_str(buffer, operator_buffer, strlen(operator_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { + yp_buffer_append_str(buffer, "InstanceVariableReadNode(", 25); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_INSTANCE_VARIABLE_WRITE_NODE: { + yp_buffer_append_str(buffer, "InstanceVariableWriteNode(", 26); + prettyprint_location(buffer, parser, &((yp_instance_variable_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_instance_variable_write_node_t *)node)->value == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_instance_variable_write_node_t *)node)->value); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_instance_variable_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_instance_variable_write_node_t *)node)->operator_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_INTEGER_NODE: { + yp_buffer_append_str(buffer, "IntegerNode(", 12); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + yp_buffer_append_str(buffer, "InterpolatedRegularExpressionNode(", 34); + prettyprint_location(buffer, parser, &((yp_interpolated_regular_expression_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_interpolated_regular_expression_node_t *)node)->parts.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_interpolated_regular_expression_node_t *) node)->parts.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_interpolated_regular_expression_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ", ", 2); char flags_buffer[12]; + snprintf(flags_buffer, sizeof(flags_buffer), "+%d", ((yp_interpolated_regular_expression_node_t *)node)->flags); + yp_buffer_append_str(buffer, flags_buffer, strlen(flags_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_INTERPOLATED_STRING_NODE: { + yp_buffer_append_str(buffer, "InterpolatedStringNode(", 23); + if (((yp_interpolated_string_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_interpolated_string_node_t *)node)->opening_loc); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_interpolated_string_node_t *)node)->parts.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_interpolated_string_node_t *) node)->parts.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_interpolated_string_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_interpolated_string_node_t *)node)->closing_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_INTERPOLATED_SYMBOL_NODE: { + yp_buffer_append_str(buffer, "InterpolatedSymbolNode(", 23); + if (((yp_interpolated_symbol_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_interpolated_symbol_node_t *)node)->opening_loc); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_interpolated_symbol_node_t *)node)->parts.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_interpolated_symbol_node_t *) node)->parts.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_interpolated_symbol_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_interpolated_symbol_node_t *)node)->closing_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_INTERPOLATED_X_STRING_NODE: { + yp_buffer_append_str(buffer, "InterpolatedXStringNode(", 24); + prettyprint_location(buffer, parser, &((yp_interpolated_x_string_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_interpolated_x_string_node_t *)node)->parts.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_interpolated_x_string_node_t *) node)->parts.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_interpolated_x_string_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_KEYWORD_HASH_NODE: { + yp_buffer_append_str(buffer, "KeywordHashNode(", 16); + for (uint32_t index = 0; index < ((yp_keyword_hash_node_t *)node)->elements.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_keyword_hash_node_t *) node)->elements.nodes[index]); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_KEYWORD_PARAMETER_NODE: { + yp_buffer_append_str(buffer, "KeywordParameterNode(", 21); + prettyprint_location(buffer, parser, &((yp_keyword_parameter_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_keyword_parameter_node_t *)node)->value == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_keyword_parameter_node_t *)node)->value); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_KEYWORD_REST_PARAMETER_NODE: { + yp_buffer_append_str(buffer, "KeywordRestParameterNode(", 25); + prettyprint_location(buffer, parser, &((yp_keyword_rest_parameter_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_keyword_rest_parameter_node_t *)node)->name_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_keyword_rest_parameter_node_t *)node)->name_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_LAMBDA_NODE: { + yp_buffer_append_str(buffer, "LambdaNode(", 11); + for (uint32_t index = 0; index < ((yp_lambda_node_t *)node)->locals.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + char locals_buffer[12]; + snprintf(locals_buffer, sizeof(locals_buffer), "%u", ((yp_lambda_node_t *)node)->locals.ids[index]); + yp_buffer_append_str(buffer, locals_buffer, strlen(locals_buffer)); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_lambda_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_lambda_node_t *)node)->parameters == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_lambda_node_t *)node)->parameters); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_lambda_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_lambda_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_LOCAL_VARIABLE_OPERATOR_AND_WRITE_NODE: { + yp_buffer_append_str(buffer, "LocalVariableOperatorAndWriteNode(", 34); + prettyprint_location(buffer, parser, &((yp_local_variable_operator_and_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_local_variable_operator_and_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_local_variable_operator_and_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); char constant_id_buffer[12]; + snprintf(constant_id_buffer, sizeof(constant_id_buffer), "%u", ((yp_local_variable_operator_and_write_node_t *)node)->constant_id); + yp_buffer_append_str(buffer, constant_id_buffer, strlen(constant_id_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_LOCAL_VARIABLE_OPERATOR_OR_WRITE_NODE: { + yp_buffer_append_str(buffer, "LocalVariableOperatorOrWriteNode(", 33); + prettyprint_location(buffer, parser, &((yp_local_variable_operator_or_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_local_variable_operator_or_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_local_variable_operator_or_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); char constant_id_buffer[12]; + snprintf(constant_id_buffer, sizeof(constant_id_buffer), "%u", ((yp_local_variable_operator_or_write_node_t *)node)->constant_id); + yp_buffer_append_str(buffer, constant_id_buffer, strlen(constant_id_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + yp_buffer_append_str(buffer, "LocalVariableOperatorWriteNode(", 31); + prettyprint_location(buffer, parser, &((yp_local_variable_operator_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_local_variable_operator_write_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_local_variable_operator_write_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); char constant_id_buffer[12]; + snprintf(constant_id_buffer, sizeof(constant_id_buffer), "%u", ((yp_local_variable_operator_write_node_t *)node)->constant_id); + yp_buffer_append_str(buffer, constant_id_buffer, strlen(constant_id_buffer)); + yp_buffer_append_str(buffer, ", ", 2); char operator_id_buffer[12]; + snprintf(operator_id_buffer, sizeof(operator_id_buffer), "%u", ((yp_local_variable_operator_write_node_t *)node)->operator_id); + yp_buffer_append_str(buffer, operator_id_buffer, strlen(operator_id_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_LOCAL_VARIABLE_READ_NODE: { + yp_buffer_append_str(buffer, "LocalVariableReadNode(", 22); + char constant_id_buffer[12]; + snprintf(constant_id_buffer, sizeof(constant_id_buffer), "%u", ((yp_local_variable_read_node_t *)node)->constant_id); + yp_buffer_append_str(buffer, constant_id_buffer, strlen(constant_id_buffer)); + yp_buffer_append_str(buffer, ", ", 2); char depth_buffer[12]; + snprintf(depth_buffer, sizeof(depth_buffer), "+%d", ((yp_local_variable_read_node_t *)node)->depth); + yp_buffer_append_str(buffer, depth_buffer, strlen(depth_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_LOCAL_VARIABLE_WRITE_NODE: { + yp_buffer_append_str(buffer, "LocalVariableWriteNode(", 23); + char constant_id_buffer[12]; + snprintf(constant_id_buffer, sizeof(constant_id_buffer), "%u", ((yp_local_variable_write_node_t *)node)->constant_id); + yp_buffer_append_str(buffer, constant_id_buffer, strlen(constant_id_buffer)); + yp_buffer_append_str(buffer, ", ", 2); char depth_buffer[12]; + snprintf(depth_buffer, sizeof(depth_buffer), "+%d", ((yp_local_variable_write_node_t *)node)->depth); + yp_buffer_append_str(buffer, depth_buffer, strlen(depth_buffer)); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_local_variable_write_node_t *)node)->value == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_local_variable_write_node_t *)node)->value); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_local_variable_write_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_local_variable_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_local_variable_write_node_t *)node)->operator_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_MATCH_PREDICATE_NODE: { + yp_buffer_append_str(buffer, "MatchPredicateNode(", 19); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->pattern); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_match_predicate_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_MATCH_REQUIRED_NODE: { + yp_buffer_append_str(buffer, "MatchRequiredNode(", 18); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_match_required_node_t *)node)->value); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_match_required_node_t *)node)->pattern); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_match_required_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_MISSING_NODE: { + yp_buffer_append_str(buffer, "MissingNode(", 12); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_MODULE_NODE: { + yp_buffer_append_str(buffer, "ModuleNode(", 11); + for (uint32_t index = 0; index < ((yp_module_node_t *)node)->locals.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + char locals_buffer[12]; + snprintf(locals_buffer, sizeof(locals_buffer), "%u", ((yp_module_node_t *)node)->locals.ids[index]); + yp_buffer_append_str(buffer, locals_buffer, strlen(locals_buffer)); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_module_node_t *)node)->module_keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_module_node_t *)node)->constant_path); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_module_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_module_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_module_node_t *)node)->end_keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_MULTI_WRITE_NODE: { + yp_buffer_append_str(buffer, "MultiWriteNode(", 15); + for (uint32_t index = 0; index < ((yp_multi_write_node_t *)node)->targets.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_multi_write_node_t *) node)->targets.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_multi_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_multi_write_node_t *)node)->operator_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_multi_write_node_t *)node)->value == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_multi_write_node_t *)node)->value); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_multi_write_node_t *)node)->lparen_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_multi_write_node_t *)node)->lparen_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_multi_write_node_t *)node)->rparen_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_multi_write_node_t *)node)->rparen_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_NEXT_NODE: { + yp_buffer_append_str(buffer, "NextNode(", 9); + if (((yp_next_node_t *)node)->arguments == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_next_node_t *)node)->arguments); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_next_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_NIL_NODE: { + yp_buffer_append_str(buffer, "NilNode(", 8); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_NO_KEYWORDS_PARAMETER_NODE: { + yp_buffer_append_str(buffer, "NoKeywordsParameterNode(", 24); + prettyprint_location(buffer, parser, &((yp_no_keywords_parameter_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_no_keywords_parameter_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_NUMBERED_REFERENCE_READ_NODE: { + yp_buffer_append_str(buffer, "NumberedReferenceReadNode(", 26); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_OPTIONAL_PARAMETER_NODE: { + yp_buffer_append_str(buffer, "OptionalParameterNode(", 22); + char constant_id_buffer[12]; + snprintf(constant_id_buffer, sizeof(constant_id_buffer), "%u", ((yp_optional_parameter_node_t *)node)->constant_id); + yp_buffer_append_str(buffer, constant_id_buffer, strlen(constant_id_buffer)); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_optional_parameter_node_t *)node)->name_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_optional_parameter_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_optional_parameter_node_t *)node)->value); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_OR_NODE: { + yp_buffer_append_str(buffer, "OrNode(", 7); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_or_node_t *)node)->left); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_or_node_t *)node)->right); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_or_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_PARAMETERS_NODE: { + yp_buffer_append_str(buffer, "ParametersNode(", 15); + for (uint32_t index = 0; index < ((yp_parameters_node_t *)node)->requireds.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_parameters_node_t *) node)->requireds.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_parameters_node_t *)node)->optionals.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_parameters_node_t *) node)->optionals.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_parameters_node_t *)node)->posts.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_parameters_node_t *) node)->posts.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_parameters_node_t *)node)->rest == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_parameters_node_t *)node)->rest); + } + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_parameters_node_t *)node)->keywords.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_parameters_node_t *) node)->keywords.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_parameters_node_t *)node)->keyword_rest == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_parameters_node_t *)node)->keyword_rest); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_parameters_node_t *)node)->block == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_parameters_node_t *)node)->block); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_PARENTHESES_NODE: { + yp_buffer_append_str(buffer, "ParenthesesNode(", 16); + if (((yp_parentheses_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_parentheses_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_parentheses_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_parentheses_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_PINNED_EXPRESSION_NODE: { + yp_buffer_append_str(buffer, "PinnedExpressionNode(", 21); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_pinned_expression_node_t *)node)->expression); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pinned_expression_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pinned_expression_node_t *)node)->lparen_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pinned_expression_node_t *)node)->rparen_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_PINNED_VARIABLE_NODE: { + yp_buffer_append_str(buffer, "PinnedVariableNode(", 19); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_pinned_variable_node_t *)node)->variable); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pinned_variable_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_POST_EXECUTION_NODE: { + yp_buffer_append_str(buffer, "PostExecutionNode(", 18); + if (((yp_post_execution_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_post_execution_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_post_execution_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_post_execution_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_post_execution_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_PRE_EXECUTION_NODE: { + yp_buffer_append_str(buffer, "PreExecutionNode(", 17); + if (((yp_pre_execution_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_pre_execution_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pre_execution_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pre_execution_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pre_execution_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_PROGRAM_NODE: { + yp_buffer_append_str(buffer, "ProgramNode(", 12); + for (uint32_t index = 0; index < ((yp_program_node_t *)node)->locals.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + char locals_buffer[12]; + snprintf(locals_buffer, sizeof(locals_buffer), "%u", ((yp_program_node_t *)node)->locals.ids[index]); + yp_buffer_append_str(buffer, locals_buffer, strlen(locals_buffer)); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_program_node_t *)node)->statements); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_RANGE_NODE: { + yp_buffer_append_str(buffer, "RangeNode(", 10); + if (((yp_range_node_t *)node)->left == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_range_node_t *)node)->left); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_range_node_t *)node)->right == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_range_node_t *)node)->right); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_range_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); char flags_buffer[12]; + snprintf(flags_buffer, sizeof(flags_buffer), "+%d", ((yp_range_node_t *)node)->flags); + yp_buffer_append_str(buffer, flags_buffer, strlen(flags_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_RATIONAL_NODE: { + yp_buffer_append_str(buffer, "RationalNode(", 13); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_rational_node_t *)node)->numeric); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_REDO_NODE: { + yp_buffer_append_str(buffer, "RedoNode(", 9); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_REGULAR_EXPRESSION_NODE: { + yp_buffer_append_str(buffer, "RegularExpressionNode(", 22); + prettyprint_location(buffer, parser, &((yp_regular_expression_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_regular_expression_node_t *)node)->content_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_regular_expression_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ", ", 2); yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, yp_string_source(&((yp_regular_expression_node_t *)node)->unescaped), yp_string_length(&((yp_regular_expression_node_t *)node)->unescaped)); + yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, ", ", 2); char flags_buffer[12]; + snprintf(flags_buffer, sizeof(flags_buffer), "+%d", ((yp_regular_expression_node_t *)node)->flags); + yp_buffer_append_str(buffer, flags_buffer, strlen(flags_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE: { + yp_buffer_append_str(buffer, "RequiredDestructuredParameterNode(", 34); + for (uint32_t index = 0; index < ((yp_required_destructured_parameter_node_t *)node)->parameters.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_required_destructured_parameter_node_t *) node)->parameters.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_required_destructured_parameter_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_required_destructured_parameter_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_REQUIRED_PARAMETER_NODE: { + yp_buffer_append_str(buffer, "RequiredParameterNode(", 22); + char constant_id_buffer[12]; + snprintf(constant_id_buffer, sizeof(constant_id_buffer), "%u", ((yp_required_parameter_node_t *)node)->constant_id); + yp_buffer_append_str(buffer, constant_id_buffer, strlen(constant_id_buffer)); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_RESCUE_MODIFIER_NODE: { + yp_buffer_append_str(buffer, "RescueModifierNode(", 19); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->expression); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_rescue_modifier_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->rescue_expression); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_RESCUE_NODE: { + yp_buffer_append_str(buffer, "RescueNode(", 11); + prettyprint_location(buffer, parser, &((yp_rescue_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_rescue_node_t *)node)->exceptions.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_rescue_node_t *) node)->exceptions.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_rescue_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_rescue_node_t *)node)->operator_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_rescue_node_t *)node)->exception == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_rescue_node_t *)node)->exception); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_rescue_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_rescue_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_rescue_node_t *)node)->consequent == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_rescue_node_t *)node)->consequent); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_REST_PARAMETER_NODE: { + yp_buffer_append_str(buffer, "RestParameterNode(", 18); + prettyprint_location(buffer, parser, &((yp_rest_parameter_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_rest_parameter_node_t *)node)->name_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_rest_parameter_node_t *)node)->name_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_RETRY_NODE: { + yp_buffer_append_str(buffer, "RetryNode(", 10); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_RETURN_NODE: { + yp_buffer_append_str(buffer, "ReturnNode(", 11); + prettyprint_location(buffer, parser, &((yp_return_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_return_node_t *)node)->arguments == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_return_node_t *)node)->arguments); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_SELF_NODE: { + yp_buffer_append_str(buffer, "SelfNode(", 9); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_SINGLETON_CLASS_NODE: { + yp_buffer_append_str(buffer, "SingletonClassNode(", 19); + for (uint32_t index = 0; index < ((yp_singleton_class_node_t *)node)->locals.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + char locals_buffer[12]; + snprintf(locals_buffer, sizeof(locals_buffer), "%u", ((yp_singleton_class_node_t *)node)->locals.ids[index]); + yp_buffer_append_str(buffer, locals_buffer, strlen(locals_buffer)); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_singleton_class_node_t *)node)->class_keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_singleton_class_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->expression); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_singleton_class_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_singleton_class_node_t *)node)->end_keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_SOURCE_ENCODING_NODE: { + yp_buffer_append_str(buffer, "SourceEncodingNode(", 19); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_SOURCE_FILE_NODE: { + yp_buffer_append_str(buffer, "SourceFileNode(", 15); + yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, yp_string_source(&((yp_source_file_node_t *)node)->filepath), yp_string_length(&((yp_source_file_node_t *)node)->filepath)); + yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_SOURCE_LINE_NODE: { + yp_buffer_append_str(buffer, "SourceLineNode(", 15); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_SPLAT_NODE: { + yp_buffer_append_str(buffer, "SplatNode(", 10); + prettyprint_location(buffer, parser, &((yp_splat_node_t *)node)->operator_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_splat_node_t *)node)->expression == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_splat_node_t *)node)->expression); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_STATEMENTS_NODE: { + yp_buffer_append_str(buffer, "StatementsNode(", 15); + for (uint32_t index = 0; index < ((yp_statements_node_t *)node)->body.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_statements_node_t *) node)->body.nodes[index]); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_STRING_CONCAT_NODE: { + yp_buffer_append_str(buffer, "StringConcatNode(", 17); + prettyprint_node(buffer, parser, (yp_node_t *)((yp_string_concat_node_t *)node)->left); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_string_concat_node_t *)node)->right); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_STRING_NODE: { + yp_buffer_append_str(buffer, "StringNode(", 11); + if (((yp_string_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_string_node_t *)node)->opening_loc); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_string_node_t *)node)->content_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_string_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_string_node_t *)node)->closing_loc); + } + yp_buffer_append_str(buffer, ", ", 2); yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, yp_string_source(&((yp_string_node_t *)node)->unescaped), yp_string_length(&((yp_string_node_t *)node)->unescaped)); + yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_SUPER_NODE: { + yp_buffer_append_str(buffer, "SuperNode(", 10); + prettyprint_location(buffer, parser, &((yp_super_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_super_node_t *)node)->lparen_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_super_node_t *)node)->lparen_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_super_node_t *)node)->arguments == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_super_node_t *)node)->arguments); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_super_node_t *)node)->rparen_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_super_node_t *)node)->rparen_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_super_node_t *)node)->block == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_super_node_t *)node)->block); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_SYMBOL_NODE: { + yp_buffer_append_str(buffer, "SymbolNode(", 11); + if (((yp_symbol_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_symbol_node_t *)node)->opening_loc); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_symbol_node_t *)node)->value_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_symbol_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_symbol_node_t *)node)->closing_loc); + } + yp_buffer_append_str(buffer, ", ", 2); yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, yp_string_source(&((yp_symbol_node_t *)node)->unescaped), yp_string_length(&((yp_symbol_node_t *)node)->unescaped)); + yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_TRUE_NODE: { + yp_buffer_append_str(buffer, "TrueNode(", 9); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_UNDEF_NODE: { + yp_buffer_append_str(buffer, "UndefNode(", 10); + for (uint32_t index = 0; index < ((yp_undef_node_t *)node)->names.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_undef_node_t *) node)->names.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_undef_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_UNLESS_NODE: { + yp_buffer_append_str(buffer, "UnlessNode(", 11); + prettyprint_location(buffer, parser, &((yp_unless_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_unless_node_t *)node)->predicate); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_unless_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_unless_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_unless_node_t *)node)->consequent == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_unless_node_t *)node)->consequent); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_unless_node_t *)node)->end_keyword_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_unless_node_t *)node)->end_keyword_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_UNTIL_NODE: { + yp_buffer_append_str(buffer, "UntilNode(", 10); + prettyprint_location(buffer, parser, &((yp_until_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_until_node_t *)node)->predicate); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_until_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_until_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_WHEN_NODE: { + yp_buffer_append_str(buffer, "WhenNode(", 9); + prettyprint_location(buffer, parser, &((yp_when_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_when_node_t *)node)->conditions.size; index++) { + if (index != 0) yp_buffer_append_str(buffer, ", ", 2); + prettyprint_node(buffer, parser, (yp_node_t *) ((yp_when_node_t *) node)->conditions.nodes[index]); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_when_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_when_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_WHILE_NODE: { + yp_buffer_append_str(buffer, "WhileNode(", 10); + prettyprint_location(buffer, parser, &((yp_while_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_while_node_t *)node)->predicate); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_while_node_t *)node)->statements == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_while_node_t *)node)->statements); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_X_STRING_NODE: { + yp_buffer_append_str(buffer, "XStringNode(", 12); + prettyprint_location(buffer, parser, &((yp_x_string_node_t *)node)->opening_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_x_string_node_t *)node)->content_loc); + yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_x_string_node_t *)node)->closing_loc); + yp_buffer_append_str(buffer, ", ", 2); yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, yp_string_source(&((yp_x_string_node_t *)node)->unescaped), yp_string_length(&((yp_x_string_node_t *)node)->unescaped)); + yp_buffer_append_str(buffer, "\"", 1); + yp_buffer_append_str(buffer, ")", 1); + break; + } + case YP_NODE_YIELD_NODE: { + yp_buffer_append_str(buffer, "YieldNode(", 10); + prettyprint_location(buffer, parser, &((yp_yield_node_t *)node)->keyword_loc); + yp_buffer_append_str(buffer, ", ", 2); if (((yp_yield_node_t *)node)->lparen_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_yield_node_t *)node)->lparen_loc); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_yield_node_t *)node)->arguments == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_node(buffer, parser, (yp_node_t *)((yp_yield_node_t *)node)->arguments); + } + yp_buffer_append_str(buffer, ", ", 2); if (((yp_yield_node_t *)node)->rparen_loc.start == NULL) { + yp_buffer_append_str(buffer, "nil", 3); + } else { + prettyprint_location(buffer, parser, &((yp_yield_node_t *)node)->rparen_loc); + } + yp_buffer_append_str(buffer, ")", 1); + break; + } + } +} + +void +yp_print_node(yp_parser_t *parser, yp_node_t *node) { + yp_buffer_t buffer; + if (!yp_buffer_init(&buffer)) return; + + prettyprint_node(&buffer, parser, node); + printf("%.*s\n", (int) buffer.length, buffer.value); + + yp_buffer_free(&buffer); +} + +// Pretty-prints the AST represented by the given node to the given buffer. +YP_EXPORTED_FUNCTION void +yp_prettyprint(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer) { + prettyprint_node(buffer, parser, node); +} diff --git a/src/main/c/yarp/src/regexp.c b/src/main/c/yarp/src/regexp.c new file mode 100644 index 000000000000..c8aeaa0adb5d --- /dev/null +++ b/src/main/c/yarp/src/regexp.c @@ -0,0 +1,547 @@ +#include "yarp/regexp.h" + +// This is the parser that is going to handle parsing regular expressions. +typedef struct { + const char *start; + const char *cursor; + const char *end; + yp_string_list_t *named_captures; +} yp_regexp_parser_t; + +// This initializes a new parser with the given source. +static void +yp_regexp_parser_init(yp_regexp_parser_t *parser, const char *start, const char *end, yp_string_list_t *named_captures) { + *parser = (yp_regexp_parser_t) { + .start = start, + .cursor = start, + .end = end, + .named_captures = named_captures + }; +} + +// This appends a new string to the list of named captures. +static void +yp_regexp_parser_named_capture(yp_regexp_parser_t *parser, const char *start, const char *end) { + yp_string_t string; + yp_string_shared_init(&string, start, end); + yp_string_list_append(parser->named_captures, &string); + yp_string_free(&string); +} + +// Returns true if the next character is the end of the source. +static inline bool +yp_regexp_char_is_eof(yp_regexp_parser_t *parser) { + return parser->cursor >= parser->end; +} + +// Optionally accept a char and consume it if it exists. +static inline bool +yp_regexp_char_accept(yp_regexp_parser_t *parser, char value) { + if (!yp_regexp_char_is_eof(parser) && *parser->cursor == value) { + parser->cursor++; + return true; + } + return false; +} + +// Expect a character to be present and consume it. +static inline bool +yp_regexp_char_expect(yp_regexp_parser_t *parser, char value) { + if (!yp_regexp_char_is_eof(parser) && *parser->cursor == value) { + parser->cursor++; + return true; + } + return false; +} + +// This advances the current token to the next instance of the given character. +static bool +yp_regexp_char_find(yp_regexp_parser_t *parser, char value) { + if (yp_regexp_char_is_eof(parser)) { + return false; + } + const char *end = (const char *) memchr(parser->cursor, value, (size_t) (parser->end - parser->cursor)); + if (end == NULL) { + return false; + } + + parser->cursor = end + 1; + return true; +} + +// Range quantifiers are a special class of quantifiers that look like +// +// * {digit} +// * {digit,} +// * {digit,digit} +// * {,digit} +// +// Unfortunately, if there are any spaces in between, then this just becomes a +// regular character match expression and we have to backtrack. So when this +// function first starts running, we'll create a "save" point and then attempt +// to parse the quantifier. If it fails, we'll restore the save point and +// return. +// +// The properly track everything, we're going to build a little state machine. +// It looks something like the following: +// +// ┌───────┐ ┌─────────┐ ────────────┐ +// ──── lbrace ───> │ start │ ──── digit ───> │ minimum │ │ +// └───────┘ └─────────┘ <─── digit ─┘ +// │ │ │ +// ┌───────┐ │ │ rbrace +// │ comma │ <───── comma ┌──── comma ───────┘ │ +// └───────┘ V V +// │ ┌─────────┐ ┌─────────┐ +// └── digit ──> │ maximum │ ── rbrace ──> │| final |│ +// └─────────┘ └─────────┘ +// │ ^ +// └─ digit ─┘ +// +// Note that by the time we've hit this function, the lbrace has already been +// consumed so we're in the start state. +static bool +yp_regexp_parse_range_quantifier(yp_regexp_parser_t *parser) { + const char *savepoint = parser->cursor; + + enum { + YP_REGEXP_RANGE_QUANTIFIER_STATE_START, + YP_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM, + YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM, + YP_REGEXP_RANGE_QUANTIFIER_STATE_COMMA + } state = YP_REGEXP_RANGE_QUANTIFIER_STATE_START; + + while (1) { + switch (state) { + case YP_REGEXP_RANGE_QUANTIFIER_STATE_START: + switch (*parser->cursor) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + parser->cursor++; + state = YP_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM; + break; + case ',': + parser->cursor++; + state = YP_REGEXP_RANGE_QUANTIFIER_STATE_COMMA; + break; + default: + parser->cursor = savepoint; + return true; + } + break; + case YP_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM: + switch (*parser->cursor) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + parser->cursor++; + break; + case ',': + parser->cursor++; + state = YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM; + break; + case '}': + parser->cursor++; + return true; + default: + parser->cursor = savepoint; + return true; + } + break; + case YP_REGEXP_RANGE_QUANTIFIER_STATE_COMMA: + switch (*parser->cursor) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + parser->cursor++; + state = YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM; + break; + default: + parser->cursor = savepoint; + return true; + } + break; + case YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM: + switch (*parser->cursor) { + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': + parser->cursor++; + break; + case '}': + parser->cursor++; + return true; + default: + parser->cursor = savepoint; + return true; + } + break; + } + } + + return true; +} + +// quantifier : star-quantifier +// | plus-quantifier +// | optional-quantifier +// | range-quantifier +// | +// ; +static bool +yp_regexp_parse_quantifier(yp_regexp_parser_t *parser) { + switch (*parser->cursor) { + case '*': + case '+': + case '?': + parser->cursor++; + return true; + case '{': + parser->cursor++; + return yp_regexp_parse_range_quantifier(parser); + default: + // In this case there is no quantifier. + return true; + } +} + +// match-posix-class : '[' '[' ':' '^'? CHAR+ ':' ']' ']' +// ; +static bool +yp_regexp_parse_posix_class(yp_regexp_parser_t *parser) { + if (!yp_regexp_char_expect(parser, ':')) { + return false; + } + + yp_regexp_char_accept(parser, '^'); + + return ( + yp_regexp_char_find(parser, ':') && + yp_regexp_char_expect(parser, ']') && + yp_regexp_char_expect(parser, ']') + ); +} + +// Forward declaration because character sets can be nested. +static bool +yp_regexp_parse_lbracket(yp_regexp_parser_t *parser); + +// match-char-set : '[' '^'? (match-range | match-char)* ']' +// ; +static bool +yp_regexp_parse_character_set(yp_regexp_parser_t *parser) { + yp_regexp_char_accept(parser, '^'); + + while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ']') { + switch (*parser->cursor++) { + case '[': + yp_regexp_parse_lbracket(parser); + break; + case '\\': + if (!yp_regexp_char_is_eof(parser)) { + parser->cursor++; + } + break; + default: + // do nothing, we've already advanced the cursor + break; + } + } + + return yp_regexp_char_expect(parser, ']'); +} + +// A left bracket can either mean a POSIX class or a character set. +static bool +yp_regexp_parse_lbracket(yp_regexp_parser_t *parser) { + const char *reset = parser->cursor; + + if ((parser->cursor + 2 < parser->end) && parser->cursor[0] == '[' && parser->cursor[1] == ':') { + parser->cursor++; + if (yp_regexp_parse_posix_class(parser)) return true; + + parser->cursor = reset; + } + + return yp_regexp_parse_character_set(parser); +} + +// Forward declaration here since parsing groups needs to go back up the grammar +// to parse expressions within them. +static bool +yp_regexp_parse_expression(yp_regexp_parser_t *parser); + +// These are the states of the options that are configurable on the regular +// expression (or from within a group). +typedef enum { + YP_REGEXP_OPTION_STATE_INVALID, + YP_REGEXP_OPTION_STATE_TOGGLEABLE, + YP_REGEXP_OPTION_STATE_ADDABLE, + YP_REGEXP_OPTION_STATE_ADDED, + YP_REGEXP_OPTION_STATE_REMOVED +} yp_regexp_option_state_t; + +// These are the options that are configurable on the regular expression (or +// from within a group). +#define YP_REGEXP_OPTION_STATE_SLOT_MINIMUM 'a' +#define YP_REGEXP_OPTION_STATE_SLOT_MAXIMUM 'x' +#define YP_REGEXP_OPTION_STATE_SLOTS (YP_REGEXP_OPTION_STATE_SLOT_MAXIMUM - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM + 1) + +// This is the set of options that are configurable on the regular expression. +typedef struct { unsigned char values[YP_REGEXP_OPTION_STATE_SLOTS]; } yp_regexp_options_t; + +// Initialize a new set of options to their default values. +static void +yp_regexp_options_init(yp_regexp_options_t *options) { + memset(options, YP_REGEXP_OPTION_STATE_INVALID, sizeof(uint8_t) * YP_REGEXP_OPTION_STATE_SLOTS); + options->values['i' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_TOGGLEABLE; + options->values['m' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_TOGGLEABLE; + options->values['x' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_TOGGLEABLE; + options->values['d' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_ADDABLE; + options->values['a' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_ADDABLE; + options->values['u' - YP_REGEXP_OPTION_STATE_SLOT_MINIMUM] = YP_REGEXP_OPTION_STATE_ADDABLE; +} + +// Attempt to add the given option to the set of options. Returns true if it was +// added, false if it was already present. +static bool +yp_regexp_options_add(yp_regexp_options_t *options, unsigned char key) { + if (key >= YP_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= YP_REGEXP_OPTION_STATE_SLOT_MAXIMUM) { + key -= YP_REGEXP_OPTION_STATE_SLOT_MINIMUM; + + switch (options->values[key]) { + case YP_REGEXP_OPTION_STATE_INVALID: + case YP_REGEXP_OPTION_STATE_REMOVED: + return false; + case YP_REGEXP_OPTION_STATE_TOGGLEABLE: + case YP_REGEXP_OPTION_STATE_ADDABLE: + options->values[key] = YP_REGEXP_OPTION_STATE_ADDED; + return true; + case YP_REGEXP_OPTION_STATE_ADDED: + return true; + } + } + + return false; +} + +// Attempt to remove the given option from the set of options. Returns true if +// it was removed, false if it was already absent. +static bool +yp_regexp_options_remove(yp_regexp_options_t *options, unsigned char key) { + if (key >= YP_REGEXP_OPTION_STATE_SLOT_MINIMUM && key <= YP_REGEXP_OPTION_STATE_SLOT_MAXIMUM) { + key -= YP_REGEXP_OPTION_STATE_SLOT_MINIMUM; + + switch (options->values[key]) { + case YP_REGEXP_OPTION_STATE_INVALID: + case YP_REGEXP_OPTION_STATE_ADDABLE: + return false; + case YP_REGEXP_OPTION_STATE_TOGGLEABLE: + case YP_REGEXP_OPTION_STATE_ADDED: + case YP_REGEXP_OPTION_STATE_REMOVED: + options->values[key] = YP_REGEXP_OPTION_STATE_REMOVED; + return true; + } + } + + return false; +} + +// Groups can have quite a few different patterns for syntax. They basically +// just wrap a set of expressions, but they can potentially have options after a +// question mark. If there _isn't_ a question mark, then it's just a set of +// expressions. If there _is_, then here are the options: +// +// * (?#...) - inline comments +// * (?:subexp) - non-capturing group +// * (?=subexp) - positive lookahead +// * (?!subexp) - negative lookahead +// * (?>subexp) - atomic group +// * (?~subexp) - absence operator +// * (?<=subexp) - positive lookbehind +// * (?subexp) - named capturing group +// * (?'name'subexp) - named capturing group +// * (?(cond)yes-subexp) - conditional expression +// * (?(cond)yes-subexp|no-subexp) - conditional expression +// * (?imxdau-imx) - turn on and off configuration +// * (?imxdau-imx:subexp) - turn on and off configuration for an expression +// +static bool +yp_regexp_parse_group(yp_regexp_parser_t *parser) { + // First, parse any options for the group. + if (yp_regexp_char_accept(parser, '?')) { + if (yp_regexp_char_is_eof(parser)) { + return false; + } + yp_regexp_options_t options; + yp_regexp_options_init(&options); + + switch (*parser->cursor) { + case '#': { // inline comments + bool found = yp_regexp_char_find(parser, ')'); + // the close paren we found is escaped, we need to find another + while (found && (parser->start <= parser->cursor - 2) && (*(parser->cursor - 2) == '\\')) { + found = yp_regexp_char_find(parser, ')'); + } + return found; + } + case ':': // non-capturing group + case '=': // positive lookahead + case '!': // negative lookahead + case '>': // atomic group + case '~': // absence operator + parser->cursor++; + break; + case '<': + parser->cursor++; + if (yp_regexp_char_is_eof(parser)) { + return false; + } + + switch (*parser->cursor) { + case '=': // positive lookbehind + case '!': // negative lookbehind + parser->cursor++; + break; + default: { // named capture group + const char *start = parser->cursor; + if (!yp_regexp_char_find(parser, '>')) { + return false; + } + yp_regexp_parser_named_capture(parser, start, parser->cursor - 1); + break; + } + } + break; + case '\'': { // named capture group + const char *start = ++parser->cursor; + if (!yp_regexp_char_find(parser, '\'')) { + return false; + } + + yp_regexp_parser_named_capture(parser, start, parser->cursor - 1); + break; + } + case '(': // conditional expression + if (!yp_regexp_char_find(parser, ')')) { + return false; + } + break; + case 'i': case 'm': case 'x': case 'd': case 'a': case 'u': // options + while (!yp_regexp_char_is_eof(parser) && *parser->cursor != '-' && *parser->cursor != ':' && *parser->cursor != ')') { + if (!yp_regexp_options_add(&options, (unsigned char) *parser->cursor)) { + return false; + } + parser->cursor++; + } + + if (yp_regexp_char_is_eof(parser)) { + return false; + } + + // If we hit a -, then we're done parsing options. + if (*parser->cursor != '-') break; + + // Otherwise, fallthrough to the - case. + /* fallthrough */ + case '-': + parser->cursor++; + while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ':' && *parser->cursor != ')') { + if (!yp_regexp_options_remove(&options, (unsigned char) *parser->cursor)) { + return false; + } + parser->cursor++; + } + + if (yp_regexp_char_is_eof(parser)) { + return false; + } + break; + default: + return false; + } + } + + // Now, parse the expressions within this group. + while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ')') { + if (!yp_regexp_parse_expression(parser)) { + return false; + } + yp_regexp_char_accept(parser, '|'); + } + + // Finally, make sure we have a closing parenthesis. + return yp_regexp_char_expect(parser, ')'); +} + +// item : anchor +// | match-posix-class +// | match-char-set +// | match-char-class +// | match-char-prop +// | match-char +// | match-any +// | group +// | quantified +// ; +static bool +yp_regexp_parse_item(yp_regexp_parser_t *parser) { + switch (*parser->cursor++) { + case '^': + case '$': + return true; + case '\\': + if (!yp_regexp_char_is_eof(parser)) { + parser->cursor++; + } + return yp_regexp_parse_quantifier(parser); + case '(': + return yp_regexp_parse_group(parser) && yp_regexp_parse_quantifier(parser); + case '[': + return yp_regexp_parse_lbracket(parser) && yp_regexp_parse_quantifier(parser); + default: + return yp_regexp_parse_quantifier(parser); + } +} + +// expression : item+ +// ; +static bool +yp_regexp_parse_expression(yp_regexp_parser_t *parser) { + if (!yp_regexp_parse_item(parser)) { + return false; + } + + while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ')' && *parser->cursor != '|') { + if (!yp_regexp_parse_item(parser)) { + return false; + } + } + + return true; +} + +// pattern : EOF +// | expression EOF +// | expression '|' pattern +// ; +static bool +yp_regexp_parse_pattern(yp_regexp_parser_t *parser) { + return ( + ( + // Exit early if the pattern is empty. + yp_regexp_char_is_eof(parser) || + // Parse the first expression in the pattern. + yp_regexp_parse_expression(parser) + ) && + ( + // Return now if we've parsed the entire pattern. + yp_regexp_char_is_eof(parser) || + // Otherwise, we should have a pipe character. + (yp_regexp_char_expect(parser, '|') && yp_regexp_parse_pattern(parser)) + ) + ); +} + +// Parse a regular expression and extract the names of all of the named capture +// groups. +YP_EXPORTED_FUNCTION bool +yp_regexp_named_capture_group_names(const char *source, size_t size, yp_string_list_t *named_captures) { + yp_regexp_parser_t parser; + yp_regexp_parser_init(&parser, source, source + size, named_captures); + return yp_regexp_parse_pattern(&parser); +} diff --git a/src/main/c/yarp/src/serialize.c b/src/main/c/yarp/src/serialize.c new file mode 100644 index 000000000000..512dedef65e2 --- /dev/null +++ b/src/main/c/yarp/src/serialize.c @@ -0,0 +1,1532 @@ +/******************************************************************************/ +/* This file is generated by the bin/template script and should not be */ +/* modified manually. See */ +/* templates/src/serialize.c.erb */ +/* if you are looking to modify the */ +/* template */ +/******************************************************************************/ +#include "yarp/ast.h" +#include "yarp/parser.h" +#include "yarp/util/yp_buffer.h" + +#include + +static inline uint32_t +yp_long_to_u32(long value) { + assert(value >= 0 && (unsigned long)value < UINT32_MAX); + return (uint32_t) value; +} + +static inline uint32_t +yp_ulong_to_u32(unsigned long value) { + assert(value < UINT32_MAX); + return (uint32_t) value; +} + +static void +serialize_location(yp_parser_t *parser, yp_location_t *location, yp_buffer_t *buffer) { + assert(location->start); + assert(location->end); + assert(location->start <= location->end); + + yp_buffer_append_u32(buffer, yp_long_to_u32(location->start - parser->start)); + yp_buffer_append_u32(buffer, yp_long_to_u32(location->end - location->start)); +} + +void +yp_serialize_node(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer) { + yp_buffer_append_u8(buffer, node->type); + + size_t offset = buffer->length; + + serialize_location(parser, &node->location, buffer); + + switch (node->type) { + case YP_NODE_ALIAS_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_alias_node_t *)node)->new_name, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_alias_node_t *)node)->old_name, buffer); + serialize_location(parser, &((yp_alias_node_t *)node)->keyword_loc, buffer); + break; + } + case YP_NODE_ALTERNATION_PATTERN_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->left, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->right, buffer); + serialize_location(parser, &((yp_alternation_pattern_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_AND_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_and_node_t *)node)->left, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_and_node_t *)node)->right, buffer); + serialize_location(parser, &((yp_and_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_ARGUMENTS_NODE: { + uint32_t arguments_size = yp_ulong_to_u32(((yp_arguments_node_t *)node)->arguments.size); + yp_buffer_append_u32(buffer, arguments_size); + for (uint32_t index = 0; index < arguments_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_arguments_node_t *)node)->arguments.nodes[index], buffer); + } + break; + } + case YP_NODE_ARRAY_NODE: { + uint32_t elements_size = yp_ulong_to_u32(((yp_array_node_t *)node)->elements.size); + yp_buffer_append_u32(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_array_node_t *)node)->elements.nodes[index], buffer); + } + if (((yp_array_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_array_node_t *)node)->opening_loc, buffer); + } + if (((yp_array_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_array_node_t *)node)->closing_loc, buffer); + } + break; + } + case YP_NODE_ARRAY_PATTERN_NODE: { + if (((yp_array_pattern_node_t *)node)->constant == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->constant, buffer); + } + uint32_t requireds_size = yp_ulong_to_u32(((yp_array_pattern_node_t *)node)->requireds.size); + yp_buffer_append_u32(buffer, requireds_size); + for (uint32_t index = 0; index < requireds_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_array_pattern_node_t *)node)->requireds.nodes[index], buffer); + } + if (((yp_array_pattern_node_t *)node)->rest == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->rest, buffer); + } + uint32_t posts_size = yp_ulong_to_u32(((yp_array_pattern_node_t *)node)->posts.size); + yp_buffer_append_u32(buffer, posts_size); + for (uint32_t index = 0; index < posts_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_array_pattern_node_t *)node)->posts.nodes[index], buffer); + } + if (((yp_array_pattern_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_array_pattern_node_t *)node)->opening_loc, buffer); + } + if (((yp_array_pattern_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_array_pattern_node_t *)node)->closing_loc, buffer); + } + break; + } + case YP_NODE_ASSOC_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_assoc_node_t *)node)->key, buffer); + if (((yp_assoc_node_t *)node)->value == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_assoc_node_t *)node)->value, buffer); + } + if (((yp_assoc_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_assoc_node_t *)node)->operator_loc, buffer); + } + break; + } + case YP_NODE_ASSOC_SPLAT_NODE: { + if (((yp_assoc_splat_node_t *)node)->value == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_assoc_splat_node_t *)node)->value, buffer); + } + serialize_location(parser, &((yp_assoc_splat_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_BACK_REFERENCE_READ_NODE: { + break; + } + case YP_NODE_BEGIN_NODE: { + if (((yp_begin_node_t *)node)->begin_keyword_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_begin_node_t *)node)->begin_keyword_loc, buffer); + } + if (((yp_begin_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_begin_node_t *)node)->statements, buffer); + } + if (((yp_begin_node_t *)node)->rescue_clause == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_begin_node_t *)node)->rescue_clause, buffer); + } + if (((yp_begin_node_t *)node)->else_clause == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_begin_node_t *)node)->else_clause, buffer); + } + if (((yp_begin_node_t *)node)->ensure_clause == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_begin_node_t *)node)->ensure_clause, buffer); + } + if (((yp_begin_node_t *)node)->end_keyword_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_begin_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case YP_NODE_BLOCK_ARGUMENT_NODE: { + if (((yp_block_argument_node_t *)node)->expression == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_block_argument_node_t *)node)->expression, buffer); + } + serialize_location(parser, &((yp_block_argument_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_BLOCK_NODE: { + uint32_t locals_size = yp_ulong_to_u32(((yp_block_node_t *)node)->locals.size); + yp_buffer_append_u32(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_block_node_t *)node)->locals.ids[index])); + } + if (((yp_block_node_t *)node)->parameters == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_block_node_t *)node)->parameters, buffer); + } + if (((yp_block_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_block_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_block_node_t *)node)->opening_loc, buffer); + serialize_location(parser, &((yp_block_node_t *)node)->closing_loc, buffer); + break; + } + case YP_NODE_BLOCK_PARAMETER_NODE: { + if (((yp_block_parameter_node_t *)node)->name_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_block_parameter_node_t *)node)->name_loc, buffer); + } + serialize_location(parser, &((yp_block_parameter_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_BLOCK_PARAMETERS_NODE: { + if (((yp_block_parameters_node_t *)node)->parameters == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_block_parameters_node_t *)node)->parameters, buffer); + } + uint32_t locals_size = yp_ulong_to_u32(((yp_block_parameters_node_t *)node)->locals.size); + yp_buffer_append_u32(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + serialize_location(parser, &((yp_block_parameters_node_t *)node)->locals.locations[index], buffer); + } + if (((yp_block_parameters_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_block_parameters_node_t *)node)->opening_loc, buffer); + } + if (((yp_block_parameters_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_block_parameters_node_t *)node)->closing_loc, buffer); + } + break; + } + case YP_NODE_BREAK_NODE: { + if (((yp_break_node_t *)node)->arguments == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_break_node_t *)node)->arguments, buffer); + } + serialize_location(parser, &((yp_break_node_t *)node)->keyword_loc, buffer); + break; + } + case YP_NODE_CALL_NODE: { + if (((yp_call_node_t *)node)->receiver == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_call_node_t *)node)->receiver, buffer); + } + if (((yp_call_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_call_node_t *)node)->operator_loc, buffer); + } + if (((yp_call_node_t *)node)->message_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_call_node_t *)node)->message_loc, buffer); + } + if (((yp_call_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_call_node_t *)node)->opening_loc, buffer); + } + if (((yp_call_node_t *)node)->arguments == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_call_node_t *)node)->arguments, buffer); + } + if (((yp_call_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_call_node_t *)node)->closing_loc, buffer); + } + if (((yp_call_node_t *)node)->block == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_call_node_t *)node)->block, buffer); + } + yp_buffer_append_u32(buffer, ((yp_call_node_t *)node)->flags); + uint32_t name_length = yp_ulong_to_u32(yp_string_length(&((yp_call_node_t *)node)->name)); + yp_buffer_append_u32(buffer, name_length); + yp_buffer_append_str(buffer, yp_string_source(&((yp_call_node_t *)node)->name), name_length); + break; + } + case YP_NODE_CALL_OPERATOR_AND_WRITE_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_call_operator_and_write_node_t *)node)->target, buffer); + serialize_location(parser, &((yp_call_operator_and_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_call_operator_and_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_CALL_OPERATOR_OR_WRITE_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_call_operator_or_write_node_t *)node)->target, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_call_operator_or_write_node_t *)node)->value, buffer); + serialize_location(parser, &((yp_call_operator_or_write_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_CALL_OPERATOR_WRITE_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_call_operator_write_node_t *)node)->target, buffer); + serialize_location(parser, &((yp_call_operator_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_call_operator_write_node_t *)node)->value, buffer); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_call_operator_write_node_t *)node)->operator_id)); + break; + } + case YP_NODE_CAPTURE_PATTERN_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->value, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->target, buffer); + serialize_location(parser, &((yp_capture_pattern_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_CASE_NODE: { + if (((yp_case_node_t *)node)->predicate == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_case_node_t *)node)->predicate, buffer); + } + uint32_t conditions_size = yp_ulong_to_u32(((yp_case_node_t *)node)->conditions.size); + yp_buffer_append_u32(buffer, conditions_size); + for (uint32_t index = 0; index < conditions_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_case_node_t *)node)->conditions.nodes[index], buffer); + } + if (((yp_case_node_t *)node)->consequent == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_case_node_t *)node)->consequent, buffer); + } + serialize_location(parser, &((yp_case_node_t *)node)->case_keyword_loc, buffer); + serialize_location(parser, &((yp_case_node_t *)node)->end_keyword_loc, buffer); + break; + } + case YP_NODE_CLASS_NODE: { + uint32_t locals_size = yp_ulong_to_u32(((yp_class_node_t *)node)->locals.size); + yp_buffer_append_u32(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_class_node_t *)node)->locals.ids[index])); + } + serialize_location(parser, &((yp_class_node_t *)node)->class_keyword_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_class_node_t *)node)->constant_path, buffer); + if (((yp_class_node_t *)node)->inheritance_operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_class_node_t *)node)->inheritance_operator_loc, buffer); + } + if (((yp_class_node_t *)node)->superclass == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_class_node_t *)node)->superclass, buffer); + } + if (((yp_class_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_class_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_class_node_t *)node)->end_keyword_loc, buffer); + break; + } + case YP_NODE_CLASS_VARIABLE_OPERATOR_AND_WRITE_NODE: { + serialize_location(parser, &((yp_class_variable_operator_and_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_class_variable_operator_and_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_class_variable_operator_and_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_CLASS_VARIABLE_OPERATOR_OR_WRITE_NODE: { + serialize_location(parser, &((yp_class_variable_operator_or_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_class_variable_operator_or_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_class_variable_operator_or_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_CLASS_VARIABLE_OPERATOR_WRITE_NODE: { + serialize_location(parser, &((yp_class_variable_operator_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_class_variable_operator_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_class_variable_operator_write_node_t *)node)->value, buffer); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_class_variable_operator_write_node_t *)node)->operator)); + break; + } + case YP_NODE_CLASS_VARIABLE_READ_NODE: { + break; + } + case YP_NODE_CLASS_VARIABLE_WRITE_NODE: { + serialize_location(parser, &((yp_class_variable_write_node_t *)node)->name_loc, buffer); + if (((yp_class_variable_write_node_t *)node)->value == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_class_variable_write_node_t *)node)->value, buffer); + } + if (((yp_class_variable_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_class_variable_write_node_t *)node)->operator_loc, buffer); + } + break; + } + case YP_NODE_CONSTANT_OPERATOR_AND_WRITE_NODE: { + serialize_location(parser, &((yp_constant_operator_and_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_constant_operator_and_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_constant_operator_and_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_CONSTANT_OPERATOR_OR_WRITE_NODE: { + serialize_location(parser, &((yp_constant_operator_or_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_constant_operator_or_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_constant_operator_or_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_CONSTANT_OPERATOR_WRITE_NODE: { + serialize_location(parser, &((yp_constant_operator_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_constant_operator_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_constant_operator_write_node_t *)node)->value, buffer); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_constant_operator_write_node_t *)node)->operator)); + break; + } + case YP_NODE_CONSTANT_PATH_NODE: { + if (((yp_constant_path_node_t *)node)->parent == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_node_t *)node)->parent, buffer); + } + yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_node_t *)node)->child, buffer); + serialize_location(parser, &((yp_constant_path_node_t *)node)->delimiter_loc, buffer); + break; + } + case YP_NODE_CONSTANT_PATH_OPERATOR_AND_WRITE_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_operator_and_write_node_t *)node)->target, buffer); + serialize_location(parser, &((yp_constant_path_operator_and_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_operator_and_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_CONSTANT_PATH_OPERATOR_OR_WRITE_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_operator_or_write_node_t *)node)->target, buffer); + serialize_location(parser, &((yp_constant_path_operator_or_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_operator_or_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_CONSTANT_PATH_OPERATOR_WRITE_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_operator_write_node_t *)node)->target, buffer); + serialize_location(parser, &((yp_constant_path_operator_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_operator_write_node_t *)node)->value, buffer); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_constant_path_operator_write_node_t *)node)->operator)); + break; + } + case YP_NODE_CONSTANT_PATH_WRITE_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->target, buffer); + if (((yp_constant_path_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_constant_path_write_node_t *)node)->operator_loc, buffer); + } + if (((yp_constant_path_write_node_t *)node)->value == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->value, buffer); + } + break; + } + case YP_NODE_CONSTANT_READ_NODE: { + break; + } + case YP_NODE_DEF_NODE: { + // serialize length + // encoding of location u32s make us need to save this offset. + size_t length_offset = buffer->length; + yp_buffer_append_str(buffer, "\0\0\0\0", 4); /* consume 4 bytes, updated below */ + serialize_location(parser, &((yp_def_node_t *)node)->name_loc, buffer); + if (((yp_def_node_t *)node)->receiver == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_def_node_t *)node)->receiver, buffer); + } + if (((yp_def_node_t *)node)->parameters == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_def_node_t *)node)->parameters, buffer); + } + if (((yp_def_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_def_node_t *)node)->statements, buffer); + } + uint32_t locals_size = yp_ulong_to_u32(((yp_def_node_t *)node)->locals.size); + yp_buffer_append_u32(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_def_node_t *)node)->locals.ids[index])); + } + serialize_location(parser, &((yp_def_node_t *)node)->def_keyword_loc, buffer); + if (((yp_def_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_def_node_t *)node)->operator_loc, buffer); + } + if (((yp_def_node_t *)node)->lparen_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_def_node_t *)node)->lparen_loc, buffer); + } + if (((yp_def_node_t *)node)->rparen_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_def_node_t *)node)->rparen_loc, buffer); + } + if (((yp_def_node_t *)node)->equal_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_def_node_t *)node)->equal_loc, buffer); + } + if (((yp_def_node_t *)node)->end_keyword_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_def_node_t *)node)->end_keyword_loc, buffer); + } + // serialize length + uint32_t length = yp_ulong_to_u32(buffer->length - offset - sizeof(uint32_t)); + memcpy(buffer->value + length_offset, &length, sizeof(uint32_t)); + break; + } + case YP_NODE_DEFINED_NODE: { + if (((yp_defined_node_t *)node)->lparen_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_defined_node_t *)node)->lparen_loc, buffer); + } + yp_serialize_node(parser, (yp_node_t *)((yp_defined_node_t *)node)->value, buffer); + if (((yp_defined_node_t *)node)->rparen_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_defined_node_t *)node)->rparen_loc, buffer); + } + serialize_location(parser, &((yp_defined_node_t *)node)->keyword_loc, buffer); + break; + } + case YP_NODE_ELSE_NODE: { + serialize_location(parser, &((yp_else_node_t *)node)->else_keyword_loc, buffer); + if (((yp_else_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_else_node_t *)node)->statements, buffer); + } + if (((yp_else_node_t *)node)->end_keyword_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_else_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case YP_NODE_EMBEDDED_STATEMENTS_NODE: { + serialize_location(parser, &((yp_embedded_statements_node_t *)node)->opening_loc, buffer); + if (((yp_embedded_statements_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_embedded_statements_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_embedded_statements_node_t *)node)->closing_loc, buffer); + break; + } + case YP_NODE_EMBEDDED_VARIABLE_NODE: { + serialize_location(parser, &((yp_embedded_variable_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_embedded_variable_node_t *)node)->variable, buffer); + break; + } + case YP_NODE_ENSURE_NODE: { + serialize_location(parser, &((yp_ensure_node_t *)node)->ensure_keyword_loc, buffer); + if (((yp_ensure_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_ensure_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_ensure_node_t *)node)->end_keyword_loc, buffer); + break; + } + case YP_NODE_FALSE_NODE: { + break; + } + case YP_NODE_FIND_PATTERN_NODE: { + if (((yp_find_pattern_node_t *)node)->constant == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->constant, buffer); + } + yp_serialize_node(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->left, buffer); + uint32_t requireds_size = yp_ulong_to_u32(((yp_find_pattern_node_t *)node)->requireds.size); + yp_buffer_append_u32(buffer, requireds_size); + for (uint32_t index = 0; index < requireds_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_find_pattern_node_t *)node)->requireds.nodes[index], buffer); + } + yp_serialize_node(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->right, buffer); + if (((yp_find_pattern_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_find_pattern_node_t *)node)->opening_loc, buffer); + } + if (((yp_find_pattern_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_find_pattern_node_t *)node)->closing_loc, buffer); + } + break; + } + case YP_NODE_FLOAT_NODE: { + break; + } + case YP_NODE_FOR_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_for_node_t *)node)->index, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_for_node_t *)node)->collection, buffer); + if (((yp_for_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_for_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_for_node_t *)node)->for_keyword_loc, buffer); + serialize_location(parser, &((yp_for_node_t *)node)->in_keyword_loc, buffer); + if (((yp_for_node_t *)node)->do_keyword_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_for_node_t *)node)->do_keyword_loc, buffer); + } + serialize_location(parser, &((yp_for_node_t *)node)->end_keyword_loc, buffer); + break; + } + case YP_NODE_FORWARDING_ARGUMENTS_NODE: { + break; + } + case YP_NODE_FORWARDING_PARAMETER_NODE: { + break; + } + case YP_NODE_FORWARDING_SUPER_NODE: { + if (((yp_forwarding_super_node_t *)node)->block == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_forwarding_super_node_t *)node)->block, buffer); + } + break; + } + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_AND_WRITE_NODE: { + serialize_location(parser, &((yp_global_variable_operator_and_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_global_variable_operator_and_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_global_variable_operator_and_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_OR_WRITE_NODE: { + serialize_location(parser, &((yp_global_variable_operator_or_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_global_variable_operator_or_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_global_variable_operator_or_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE: { + serialize_location(parser, &((yp_global_variable_operator_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_global_variable_operator_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_global_variable_operator_write_node_t *)node)->value, buffer); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_global_variable_operator_write_node_t *)node)->operator)); + break; + } + case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { + break; + } + case YP_NODE_GLOBAL_VARIABLE_WRITE_NODE: { + serialize_location(parser, &((yp_global_variable_write_node_t *)node)->name_loc, buffer); + if (((yp_global_variable_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_global_variable_write_node_t *)node)->operator_loc, buffer); + } + if (((yp_global_variable_write_node_t *)node)->value == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_global_variable_write_node_t *)node)->value, buffer); + } + break; + } + case YP_NODE_HASH_NODE: { + serialize_location(parser, &((yp_hash_node_t *)node)->opening_loc, buffer); + uint32_t elements_size = yp_ulong_to_u32(((yp_hash_node_t *)node)->elements.size); + yp_buffer_append_u32(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_hash_node_t *)node)->elements.nodes[index], buffer); + } + serialize_location(parser, &((yp_hash_node_t *)node)->closing_loc, buffer); + break; + } + case YP_NODE_HASH_PATTERN_NODE: { + if (((yp_hash_pattern_node_t *)node)->constant == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->constant, buffer); + } + uint32_t assocs_size = yp_ulong_to_u32(((yp_hash_pattern_node_t *)node)->assocs.size); + yp_buffer_append_u32(buffer, assocs_size); + for (uint32_t index = 0; index < assocs_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_hash_pattern_node_t *)node)->assocs.nodes[index], buffer); + } + if (((yp_hash_pattern_node_t *)node)->kwrest == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->kwrest, buffer); + } + if (((yp_hash_pattern_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_hash_pattern_node_t *)node)->opening_loc, buffer); + } + if (((yp_hash_pattern_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_hash_pattern_node_t *)node)->closing_loc, buffer); + } + break; + } + case YP_NODE_IF_NODE: { + if (((yp_if_node_t *)node)->if_keyword_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_if_node_t *)node)->if_keyword_loc, buffer); + } + yp_serialize_node(parser, (yp_node_t *)((yp_if_node_t *)node)->predicate, buffer); + if (((yp_if_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_if_node_t *)node)->statements, buffer); + } + if (((yp_if_node_t *)node)->consequent == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_if_node_t *)node)->consequent, buffer); + } + if (((yp_if_node_t *)node)->end_keyword_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_if_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case YP_NODE_IMAGINARY_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_imaginary_node_t *)node)->numeric, buffer); + break; + } + case YP_NODE_IN_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_in_node_t *)node)->pattern, buffer); + if (((yp_in_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_in_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_in_node_t *)node)->in_loc, buffer); + if (((yp_in_node_t *)node)->then_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_in_node_t *)node)->then_loc, buffer); + } + break; + } + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_AND_WRITE_NODE: { + serialize_location(parser, &((yp_instance_variable_operator_and_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_instance_variable_operator_and_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_instance_variable_operator_and_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_OR_WRITE_NODE: { + serialize_location(parser, &((yp_instance_variable_operator_or_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_instance_variable_operator_or_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_instance_variable_operator_or_write_node_t *)node)->value, buffer); + break; + } + case YP_NODE_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE: { + serialize_location(parser, &((yp_instance_variable_operator_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_instance_variable_operator_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_instance_variable_operator_write_node_t *)node)->value, buffer); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_instance_variable_operator_write_node_t *)node)->operator)); + break; + } + case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { + break; + } + case YP_NODE_INSTANCE_VARIABLE_WRITE_NODE: { + serialize_location(parser, &((yp_instance_variable_write_node_t *)node)->name_loc, buffer); + if (((yp_instance_variable_write_node_t *)node)->value == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_instance_variable_write_node_t *)node)->value, buffer); + } + if (((yp_instance_variable_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_instance_variable_write_node_t *)node)->operator_loc, buffer); + } + break; + } + case YP_NODE_INTEGER_NODE: { + break; + } + case YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE: { + serialize_location(parser, &((yp_interpolated_regular_expression_node_t *)node)->opening_loc, buffer); + uint32_t parts_size = yp_ulong_to_u32(((yp_interpolated_regular_expression_node_t *)node)->parts.size); + yp_buffer_append_u32(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_interpolated_regular_expression_node_t *)node)->parts.nodes[index], buffer); + } + serialize_location(parser, &((yp_interpolated_regular_expression_node_t *)node)->closing_loc, buffer); + yp_buffer_append_u32(buffer, ((yp_interpolated_regular_expression_node_t *)node)->flags); + break; + } + case YP_NODE_INTERPOLATED_STRING_NODE: { + if (((yp_interpolated_string_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_interpolated_string_node_t *)node)->opening_loc, buffer); + } + uint32_t parts_size = yp_ulong_to_u32(((yp_interpolated_string_node_t *)node)->parts.size); + yp_buffer_append_u32(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_interpolated_string_node_t *)node)->parts.nodes[index], buffer); + } + if (((yp_interpolated_string_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_interpolated_string_node_t *)node)->closing_loc, buffer); + } + break; + } + case YP_NODE_INTERPOLATED_SYMBOL_NODE: { + if (((yp_interpolated_symbol_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_interpolated_symbol_node_t *)node)->opening_loc, buffer); + } + uint32_t parts_size = yp_ulong_to_u32(((yp_interpolated_symbol_node_t *)node)->parts.size); + yp_buffer_append_u32(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_interpolated_symbol_node_t *)node)->parts.nodes[index], buffer); + } + if (((yp_interpolated_symbol_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_interpolated_symbol_node_t *)node)->closing_loc, buffer); + } + break; + } + case YP_NODE_INTERPOLATED_X_STRING_NODE: { + serialize_location(parser, &((yp_interpolated_x_string_node_t *)node)->opening_loc, buffer); + uint32_t parts_size = yp_ulong_to_u32(((yp_interpolated_x_string_node_t *)node)->parts.size); + yp_buffer_append_u32(buffer, parts_size); + for (uint32_t index = 0; index < parts_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_interpolated_x_string_node_t *)node)->parts.nodes[index], buffer); + } + serialize_location(parser, &((yp_interpolated_x_string_node_t *)node)->closing_loc, buffer); + break; + } + case YP_NODE_KEYWORD_HASH_NODE: { + uint32_t elements_size = yp_ulong_to_u32(((yp_keyword_hash_node_t *)node)->elements.size); + yp_buffer_append_u32(buffer, elements_size); + for (uint32_t index = 0; index < elements_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_keyword_hash_node_t *)node)->elements.nodes[index], buffer); + } + break; + } + case YP_NODE_KEYWORD_PARAMETER_NODE: { + serialize_location(parser, &((yp_keyword_parameter_node_t *)node)->name_loc, buffer); + if (((yp_keyword_parameter_node_t *)node)->value == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_keyword_parameter_node_t *)node)->value, buffer); + } + break; + } + case YP_NODE_KEYWORD_REST_PARAMETER_NODE: { + serialize_location(parser, &((yp_keyword_rest_parameter_node_t *)node)->operator_loc, buffer); + if (((yp_keyword_rest_parameter_node_t *)node)->name_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_keyword_rest_parameter_node_t *)node)->name_loc, buffer); + } + break; + } + case YP_NODE_LAMBDA_NODE: { + uint32_t locals_size = yp_ulong_to_u32(((yp_lambda_node_t *)node)->locals.size); + yp_buffer_append_u32(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_lambda_node_t *)node)->locals.ids[index])); + } + serialize_location(parser, &((yp_lambda_node_t *)node)->opening_loc, buffer); + if (((yp_lambda_node_t *)node)->parameters == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_lambda_node_t *)node)->parameters, buffer); + } + if (((yp_lambda_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_lambda_node_t *)node)->statements, buffer); + } + break; + } + case YP_NODE_LOCAL_VARIABLE_OPERATOR_AND_WRITE_NODE: { + serialize_location(parser, &((yp_local_variable_operator_and_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_local_variable_operator_and_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_local_variable_operator_and_write_node_t *)node)->value, buffer); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_local_variable_operator_and_write_node_t *)node)->constant_id)); + break; + } + case YP_NODE_LOCAL_VARIABLE_OPERATOR_OR_WRITE_NODE: { + serialize_location(parser, &((yp_local_variable_operator_or_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_local_variable_operator_or_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_local_variable_operator_or_write_node_t *)node)->value, buffer); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_local_variable_operator_or_write_node_t *)node)->constant_id)); + break; + } + case YP_NODE_LOCAL_VARIABLE_OPERATOR_WRITE_NODE: { + serialize_location(parser, &((yp_local_variable_operator_write_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_local_variable_operator_write_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_local_variable_operator_write_node_t *)node)->value, buffer); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_local_variable_operator_write_node_t *)node)->constant_id)); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_local_variable_operator_write_node_t *)node)->operator_id)); + break; + } + case YP_NODE_LOCAL_VARIABLE_READ_NODE: { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_local_variable_read_node_t *)node)->constant_id)); + yp_buffer_append_u32(buffer, ((yp_local_variable_read_node_t *)node)->depth); + break; + } + case YP_NODE_LOCAL_VARIABLE_WRITE_NODE: { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_local_variable_write_node_t *)node)->constant_id)); + yp_buffer_append_u32(buffer, ((yp_local_variable_write_node_t *)node)->depth); + if (((yp_local_variable_write_node_t *)node)->value == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_local_variable_write_node_t *)node)->value, buffer); + } + serialize_location(parser, &((yp_local_variable_write_node_t *)node)->name_loc, buffer); + if (((yp_local_variable_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_local_variable_write_node_t *)node)->operator_loc, buffer); + } + break; + } + case YP_NODE_MATCH_PREDICATE_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->value, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->pattern, buffer); + serialize_location(parser, &((yp_match_predicate_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_MATCH_REQUIRED_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_match_required_node_t *)node)->value, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_match_required_node_t *)node)->pattern, buffer); + serialize_location(parser, &((yp_match_required_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_MISSING_NODE: { + break; + } + case YP_NODE_MODULE_NODE: { + uint32_t locals_size = yp_ulong_to_u32(((yp_module_node_t *)node)->locals.size); + yp_buffer_append_u32(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_module_node_t *)node)->locals.ids[index])); + } + serialize_location(parser, &((yp_module_node_t *)node)->module_keyword_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_module_node_t *)node)->constant_path, buffer); + if (((yp_module_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_module_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_module_node_t *)node)->end_keyword_loc, buffer); + break; + } + case YP_NODE_MULTI_WRITE_NODE: { + uint32_t targets_size = yp_ulong_to_u32(((yp_multi_write_node_t *)node)->targets.size); + yp_buffer_append_u32(buffer, targets_size); + for (uint32_t index = 0; index < targets_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_multi_write_node_t *)node)->targets.nodes[index], buffer); + } + if (((yp_multi_write_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_multi_write_node_t *)node)->operator_loc, buffer); + } + if (((yp_multi_write_node_t *)node)->value == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_multi_write_node_t *)node)->value, buffer); + } + if (((yp_multi_write_node_t *)node)->lparen_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_multi_write_node_t *)node)->lparen_loc, buffer); + } + if (((yp_multi_write_node_t *)node)->rparen_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_multi_write_node_t *)node)->rparen_loc, buffer); + } + break; + } + case YP_NODE_NEXT_NODE: { + if (((yp_next_node_t *)node)->arguments == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_next_node_t *)node)->arguments, buffer); + } + serialize_location(parser, &((yp_next_node_t *)node)->keyword_loc, buffer); + break; + } + case YP_NODE_NIL_NODE: { + break; + } + case YP_NODE_NO_KEYWORDS_PARAMETER_NODE: { + serialize_location(parser, &((yp_no_keywords_parameter_node_t *)node)->operator_loc, buffer); + serialize_location(parser, &((yp_no_keywords_parameter_node_t *)node)->keyword_loc, buffer); + break; + } + case YP_NODE_NUMBERED_REFERENCE_READ_NODE: { + break; + } + case YP_NODE_OPTIONAL_PARAMETER_NODE: { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_optional_parameter_node_t *)node)->constant_id)); + serialize_location(parser, &((yp_optional_parameter_node_t *)node)->name_loc, buffer); + serialize_location(parser, &((yp_optional_parameter_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_optional_parameter_node_t *)node)->value, buffer); + break; + } + case YP_NODE_OR_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_or_node_t *)node)->left, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_or_node_t *)node)->right, buffer); + serialize_location(parser, &((yp_or_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_PARAMETERS_NODE: { + uint32_t requireds_size = yp_ulong_to_u32(((yp_parameters_node_t *)node)->requireds.size); + yp_buffer_append_u32(buffer, requireds_size); + for (uint32_t index = 0; index < requireds_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_parameters_node_t *)node)->requireds.nodes[index], buffer); + } + uint32_t optionals_size = yp_ulong_to_u32(((yp_parameters_node_t *)node)->optionals.size); + yp_buffer_append_u32(buffer, optionals_size); + for (uint32_t index = 0; index < optionals_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_parameters_node_t *)node)->optionals.nodes[index], buffer); + } + uint32_t posts_size = yp_ulong_to_u32(((yp_parameters_node_t *)node)->posts.size); + yp_buffer_append_u32(buffer, posts_size); + for (uint32_t index = 0; index < posts_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_parameters_node_t *)node)->posts.nodes[index], buffer); + } + if (((yp_parameters_node_t *)node)->rest == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_parameters_node_t *)node)->rest, buffer); + } + uint32_t keywords_size = yp_ulong_to_u32(((yp_parameters_node_t *)node)->keywords.size); + yp_buffer_append_u32(buffer, keywords_size); + for (uint32_t index = 0; index < keywords_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_parameters_node_t *)node)->keywords.nodes[index], buffer); + } + if (((yp_parameters_node_t *)node)->keyword_rest == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_parameters_node_t *)node)->keyword_rest, buffer); + } + if (((yp_parameters_node_t *)node)->block == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_parameters_node_t *)node)->block, buffer); + } + break; + } + case YP_NODE_PARENTHESES_NODE: { + if (((yp_parentheses_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_parentheses_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_parentheses_node_t *)node)->opening_loc, buffer); + serialize_location(parser, &((yp_parentheses_node_t *)node)->closing_loc, buffer); + break; + } + case YP_NODE_PINNED_EXPRESSION_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_pinned_expression_node_t *)node)->expression, buffer); + serialize_location(parser, &((yp_pinned_expression_node_t *)node)->operator_loc, buffer); + serialize_location(parser, &((yp_pinned_expression_node_t *)node)->lparen_loc, buffer); + serialize_location(parser, &((yp_pinned_expression_node_t *)node)->rparen_loc, buffer); + break; + } + case YP_NODE_PINNED_VARIABLE_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_pinned_variable_node_t *)node)->variable, buffer); + serialize_location(parser, &((yp_pinned_variable_node_t *)node)->operator_loc, buffer); + break; + } + case YP_NODE_POST_EXECUTION_NODE: { + if (((yp_post_execution_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_post_execution_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_post_execution_node_t *)node)->keyword_loc, buffer); + serialize_location(parser, &((yp_post_execution_node_t *)node)->opening_loc, buffer); + serialize_location(parser, &((yp_post_execution_node_t *)node)->closing_loc, buffer); + break; + } + case YP_NODE_PRE_EXECUTION_NODE: { + if (((yp_pre_execution_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_pre_execution_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_pre_execution_node_t *)node)->keyword_loc, buffer); + serialize_location(parser, &((yp_pre_execution_node_t *)node)->opening_loc, buffer); + serialize_location(parser, &((yp_pre_execution_node_t *)node)->closing_loc, buffer); + break; + } + case YP_NODE_PROGRAM_NODE: { + uint32_t locals_size = yp_ulong_to_u32(((yp_program_node_t *)node)->locals.size); + yp_buffer_append_u32(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_program_node_t *)node)->locals.ids[index])); + } + yp_serialize_node(parser, (yp_node_t *)((yp_program_node_t *)node)->statements, buffer); + break; + } + case YP_NODE_RANGE_NODE: { + if (((yp_range_node_t *)node)->left == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_range_node_t *)node)->left, buffer); + } + if (((yp_range_node_t *)node)->right == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_range_node_t *)node)->right, buffer); + } + serialize_location(parser, &((yp_range_node_t *)node)->operator_loc, buffer); + yp_buffer_append_u32(buffer, ((yp_range_node_t *)node)->flags); + break; + } + case YP_NODE_RATIONAL_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_rational_node_t *)node)->numeric, buffer); + break; + } + case YP_NODE_REDO_NODE: { + break; + } + case YP_NODE_REGULAR_EXPRESSION_NODE: { + serialize_location(parser, &((yp_regular_expression_node_t *)node)->opening_loc, buffer); + serialize_location(parser, &((yp_regular_expression_node_t *)node)->content_loc, buffer); + serialize_location(parser, &((yp_regular_expression_node_t *)node)->closing_loc, buffer); + uint32_t unescaped_length = yp_ulong_to_u32(yp_string_length(&((yp_regular_expression_node_t *)node)->unescaped)); + yp_buffer_append_u32(buffer, unescaped_length); + yp_buffer_append_str(buffer, yp_string_source(&((yp_regular_expression_node_t *)node)->unescaped), unescaped_length); + yp_buffer_append_u32(buffer, ((yp_regular_expression_node_t *)node)->flags); + break; + } + case YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE: { + uint32_t parameters_size = yp_ulong_to_u32(((yp_required_destructured_parameter_node_t *)node)->parameters.size); + yp_buffer_append_u32(buffer, parameters_size); + for (uint32_t index = 0; index < parameters_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_required_destructured_parameter_node_t *)node)->parameters.nodes[index], buffer); + } + serialize_location(parser, &((yp_required_destructured_parameter_node_t *)node)->opening_loc, buffer); + serialize_location(parser, &((yp_required_destructured_parameter_node_t *)node)->closing_loc, buffer); + break; + } + case YP_NODE_REQUIRED_PARAMETER_NODE: { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_required_parameter_node_t *)node)->constant_id)); + break; + } + case YP_NODE_RESCUE_MODIFIER_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->expression, buffer); + serialize_location(parser, &((yp_rescue_modifier_node_t *)node)->keyword_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->rescue_expression, buffer); + break; + } + case YP_NODE_RESCUE_NODE: { + serialize_location(parser, &((yp_rescue_node_t *)node)->keyword_loc, buffer); + uint32_t exceptions_size = yp_ulong_to_u32(((yp_rescue_node_t *)node)->exceptions.size); + yp_buffer_append_u32(buffer, exceptions_size); + for (uint32_t index = 0; index < exceptions_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_rescue_node_t *)node)->exceptions.nodes[index], buffer); + } + if (((yp_rescue_node_t *)node)->operator_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_rescue_node_t *)node)->operator_loc, buffer); + } + if (((yp_rescue_node_t *)node)->exception == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_rescue_node_t *)node)->exception, buffer); + } + if (((yp_rescue_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_rescue_node_t *)node)->statements, buffer); + } + if (((yp_rescue_node_t *)node)->consequent == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_rescue_node_t *)node)->consequent, buffer); + } + break; + } + case YP_NODE_REST_PARAMETER_NODE: { + serialize_location(parser, &((yp_rest_parameter_node_t *)node)->operator_loc, buffer); + if (((yp_rest_parameter_node_t *)node)->name_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_rest_parameter_node_t *)node)->name_loc, buffer); + } + break; + } + case YP_NODE_RETRY_NODE: { + break; + } + case YP_NODE_RETURN_NODE: { + serialize_location(parser, &((yp_return_node_t *)node)->keyword_loc, buffer); + if (((yp_return_node_t *)node)->arguments == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_return_node_t *)node)->arguments, buffer); + } + break; + } + case YP_NODE_SELF_NODE: { + break; + } + case YP_NODE_SINGLETON_CLASS_NODE: { + uint32_t locals_size = yp_ulong_to_u32(((yp_singleton_class_node_t *)node)->locals.size); + yp_buffer_append_u32(buffer, locals_size); + for (uint32_t index = 0; index < locals_size; index++) { + yp_buffer_append_u32(buffer, yp_ulong_to_u32(((yp_singleton_class_node_t *)node)->locals.ids[index])); + } + serialize_location(parser, &((yp_singleton_class_node_t *)node)->class_keyword_loc, buffer); + serialize_location(parser, &((yp_singleton_class_node_t *)node)->operator_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->expression, buffer); + if (((yp_singleton_class_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->statements, buffer); + } + serialize_location(parser, &((yp_singleton_class_node_t *)node)->end_keyword_loc, buffer); + break; + } + case YP_NODE_SOURCE_ENCODING_NODE: { + break; + } + case YP_NODE_SOURCE_FILE_NODE: { + uint32_t filepath_length = yp_ulong_to_u32(yp_string_length(&((yp_source_file_node_t *)node)->filepath)); + yp_buffer_append_u32(buffer, filepath_length); + yp_buffer_append_str(buffer, yp_string_source(&((yp_source_file_node_t *)node)->filepath), filepath_length); + break; + } + case YP_NODE_SOURCE_LINE_NODE: { + break; + } + case YP_NODE_SPLAT_NODE: { + serialize_location(parser, &((yp_splat_node_t *)node)->operator_loc, buffer); + if (((yp_splat_node_t *)node)->expression == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_splat_node_t *)node)->expression, buffer); + } + break; + } + case YP_NODE_STATEMENTS_NODE: { + uint32_t body_size = yp_ulong_to_u32(((yp_statements_node_t *)node)->body.size); + yp_buffer_append_u32(buffer, body_size); + for (uint32_t index = 0; index < body_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_statements_node_t *)node)->body.nodes[index], buffer); + } + break; + } + case YP_NODE_STRING_CONCAT_NODE: { + yp_serialize_node(parser, (yp_node_t *)((yp_string_concat_node_t *)node)->left, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_string_concat_node_t *)node)->right, buffer); + break; + } + case YP_NODE_STRING_NODE: { + if (((yp_string_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_string_node_t *)node)->opening_loc, buffer); + } + serialize_location(parser, &((yp_string_node_t *)node)->content_loc, buffer); + if (((yp_string_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_string_node_t *)node)->closing_loc, buffer); + } + uint32_t unescaped_length = yp_ulong_to_u32(yp_string_length(&((yp_string_node_t *)node)->unescaped)); + yp_buffer_append_u32(buffer, unescaped_length); + yp_buffer_append_str(buffer, yp_string_source(&((yp_string_node_t *)node)->unescaped), unescaped_length); + break; + } + case YP_NODE_SUPER_NODE: { + serialize_location(parser, &((yp_super_node_t *)node)->keyword_loc, buffer); + if (((yp_super_node_t *)node)->lparen_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_super_node_t *)node)->lparen_loc, buffer); + } + if (((yp_super_node_t *)node)->arguments == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_super_node_t *)node)->arguments, buffer); + } + if (((yp_super_node_t *)node)->rparen_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_super_node_t *)node)->rparen_loc, buffer); + } + if (((yp_super_node_t *)node)->block == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_super_node_t *)node)->block, buffer); + } + break; + } + case YP_NODE_SYMBOL_NODE: { + if (((yp_symbol_node_t *)node)->opening_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_symbol_node_t *)node)->opening_loc, buffer); + } + serialize_location(parser, &((yp_symbol_node_t *)node)->value_loc, buffer); + if (((yp_symbol_node_t *)node)->closing_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_symbol_node_t *)node)->closing_loc, buffer); + } + uint32_t unescaped_length = yp_ulong_to_u32(yp_string_length(&((yp_symbol_node_t *)node)->unescaped)); + yp_buffer_append_u32(buffer, unescaped_length); + yp_buffer_append_str(buffer, yp_string_source(&((yp_symbol_node_t *)node)->unescaped), unescaped_length); + break; + } + case YP_NODE_TRUE_NODE: { + break; + } + case YP_NODE_UNDEF_NODE: { + uint32_t names_size = yp_ulong_to_u32(((yp_undef_node_t *)node)->names.size); + yp_buffer_append_u32(buffer, names_size); + for (uint32_t index = 0; index < names_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_undef_node_t *)node)->names.nodes[index], buffer); + } + serialize_location(parser, &((yp_undef_node_t *)node)->keyword_loc, buffer); + break; + } + case YP_NODE_UNLESS_NODE: { + serialize_location(parser, &((yp_unless_node_t *)node)->keyword_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_unless_node_t *)node)->predicate, buffer); + if (((yp_unless_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_unless_node_t *)node)->statements, buffer); + } + if (((yp_unless_node_t *)node)->consequent == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_unless_node_t *)node)->consequent, buffer); + } + if (((yp_unless_node_t *)node)->end_keyword_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_unless_node_t *)node)->end_keyword_loc, buffer); + } + break; + } + case YP_NODE_UNTIL_NODE: { + serialize_location(parser, &((yp_until_node_t *)node)->keyword_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_until_node_t *)node)->predicate, buffer); + if (((yp_until_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_until_node_t *)node)->statements, buffer); + } + break; + } + case YP_NODE_WHEN_NODE: { + serialize_location(parser, &((yp_when_node_t *)node)->keyword_loc, buffer); + uint32_t conditions_size = yp_ulong_to_u32(((yp_when_node_t *)node)->conditions.size); + yp_buffer_append_u32(buffer, conditions_size); + for (uint32_t index = 0; index < conditions_size; index++) { + yp_serialize_node(parser, (yp_node_t *) ((yp_when_node_t *)node)->conditions.nodes[index], buffer); + } + if (((yp_when_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_when_node_t *)node)->statements, buffer); + } + break; + } + case YP_NODE_WHILE_NODE: { + serialize_location(parser, &((yp_while_node_t *)node)->keyword_loc, buffer); + yp_serialize_node(parser, (yp_node_t *)((yp_while_node_t *)node)->predicate, buffer); + if (((yp_while_node_t *)node)->statements == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_while_node_t *)node)->statements, buffer); + } + break; + } + case YP_NODE_X_STRING_NODE: { + serialize_location(parser, &((yp_x_string_node_t *)node)->opening_loc, buffer); + serialize_location(parser, &((yp_x_string_node_t *)node)->content_loc, buffer); + serialize_location(parser, &((yp_x_string_node_t *)node)->closing_loc, buffer); + uint32_t unescaped_length = yp_ulong_to_u32(yp_string_length(&((yp_x_string_node_t *)node)->unescaped)); + yp_buffer_append_u32(buffer, unescaped_length); + yp_buffer_append_str(buffer, yp_string_source(&((yp_x_string_node_t *)node)->unescaped), unescaped_length); + break; + } + case YP_NODE_YIELD_NODE: { + serialize_location(parser, &((yp_yield_node_t *)node)->keyword_loc, buffer); + if (((yp_yield_node_t *)node)->lparen_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_yield_node_t *)node)->lparen_loc, buffer); + } + if (((yp_yield_node_t *)node)->arguments == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_serialize_node(parser, (yp_node_t *)((yp_yield_node_t *)node)->arguments, buffer); + } + if (((yp_yield_node_t *)node)->rparen_loc.start == NULL) { + yp_buffer_append_u8(buffer, 0); + } else { + yp_buffer_append_u8(buffer, 1); + serialize_location(parser, &((yp_yield_node_t *)node)->rparen_loc, buffer); + } + break; + } + } +} + +void +yp_serialize_content(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer) { + // First, serialize the encoding of the parser. + size_t encoding_length = strlen(parser->encoding.name); + yp_buffer_append_u32(buffer, yp_ulong_to_u32(encoding_length)); + yp_buffer_append_str(buffer, parser->encoding.name, encoding_length); + + // Here we're going to leave space for the offset of the constant pool in + // the buffer. + size_t offset = buffer->length; + yp_buffer_append_zeroes(buffer, 4); + + // Next, encode the length of the constant pool. + yp_buffer_append_u32(buffer, yp_ulong_to_u32(parser->constant_pool.size)); + + // Now we're going to serialize the content of the node. + yp_serialize_node(parser, node, buffer); + + // Now we're going to serialize the offset of the constant pool back where + // we left space for it. + uint32_t length = yp_ulong_to_u32(buffer->length); + memcpy(buffer->value + offset, &length, sizeof(uint32_t)); + + // Now we're going to serialize the constant pool. + offset = buffer->length; + yp_buffer_append_zeroes(buffer, parser->constant_pool.size * 8); + + yp_constant_t *constant; + for (size_t index = 0; index < parser->constant_pool.capacity; index++) { + constant = &parser->constant_pool.constants[index]; + + // If we find a constant at this index, serialize it at the correct + // index in the buffer. + if (constant->id != 0) { + size_t buffer_offset = offset + ((constant->id - 1) * 8); + + uint32_t source_offset = yp_long_to_u32(constant->start - parser->start); + uint32_t constant_length = yp_ulong_to_u32(constant->length); + + memcpy(buffer->value + buffer_offset, &source_offset, 4); + memcpy(buffer->value + buffer_offset + 4, &constant_length, 4); + } + } +} diff --git a/src/main/c/yarp/src/token_type.c b/src/main/c/yarp/src/token_type.c new file mode 100644 index 000000000000..882eecc6a056 --- /dev/null +++ b/src/main/c/yarp/src/token_type.c @@ -0,0 +1,337 @@ +/******************************************************************************/ +/* This file is generated by the bin/template script and should not be */ +/* modified manually. See */ +/* templates/src/token_type.c.erb */ +/* if you are looking to modify the */ +/* template */ +/******************************************************************************/ +#include + +#include "yarp/ast.h" + +// Returns a string representation of the given token type. +YP_EXPORTED_FUNCTION const char * +yp_token_type_to_str(yp_token_type_t token_type) +{ + switch (token_type) { + case YP_TOKEN_EOF: + return "EOF"; + case YP_TOKEN_MISSING: + return "MISSING"; + case YP_TOKEN_NOT_PROVIDED: + return "NOT_PROVIDED"; + case YP_TOKEN_AMPERSAND: + return "AMPERSAND"; + case YP_TOKEN_AMPERSAND_AMPERSAND: + return "AMPERSAND_AMPERSAND"; + case YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL: + return "AMPERSAND_AMPERSAND_EQUAL"; + case YP_TOKEN_AMPERSAND_DOT: + return "AMPERSAND_DOT"; + case YP_TOKEN_AMPERSAND_EQUAL: + return "AMPERSAND_EQUAL"; + case YP_TOKEN_BACKTICK: + return "BACKTICK"; + case YP_TOKEN_BACK_REFERENCE: + return "BACK_REFERENCE"; + case YP_TOKEN_BANG: + return "BANG"; + case YP_TOKEN_BANG_EQUAL: + return "BANG_EQUAL"; + case YP_TOKEN_BANG_TILDE: + return "BANG_TILDE"; + case YP_TOKEN_BRACE_LEFT: + return "BRACE_LEFT"; + case YP_TOKEN_BRACE_RIGHT: + return "BRACE_RIGHT"; + case YP_TOKEN_BRACKET_LEFT: + return "BRACKET_LEFT"; + case YP_TOKEN_BRACKET_LEFT_ARRAY: + return "BRACKET_LEFT_ARRAY"; + case YP_TOKEN_BRACKET_LEFT_RIGHT: + return "BRACKET_LEFT_RIGHT"; + case YP_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: + return "BRACKET_LEFT_RIGHT_EQUAL"; + case YP_TOKEN_BRACKET_RIGHT: + return "BRACKET_RIGHT"; + case YP_TOKEN_CARET: + return "CARET"; + case YP_TOKEN_CARET_EQUAL: + return "CARET_EQUAL"; + case YP_TOKEN_CHARACTER_LITERAL: + return "CHARACTER_LITERAL"; + case YP_TOKEN_CLASS_VARIABLE: + return "CLASS_VARIABLE"; + case YP_TOKEN_COLON: + return "COLON"; + case YP_TOKEN_COLON_COLON: + return "COLON_COLON"; + case YP_TOKEN_COMMA: + return "COMMA"; + case YP_TOKEN_COMMENT: + return "COMMENT"; + case YP_TOKEN_CONSTANT: + return "CONSTANT"; + case YP_TOKEN_DOT: + return "DOT"; + case YP_TOKEN_DOT_DOT: + return "DOT_DOT"; + case YP_TOKEN_DOT_DOT_DOT: + return "DOT_DOT_DOT"; + case YP_TOKEN_EMBDOC_BEGIN: + return "EMBDOC_BEGIN"; + case YP_TOKEN_EMBDOC_END: + return "EMBDOC_END"; + case YP_TOKEN_EMBDOC_LINE: + return "EMBDOC_LINE"; + case YP_TOKEN_EMBEXPR_BEGIN: + return "EMBEXPR_BEGIN"; + case YP_TOKEN_EMBEXPR_END: + return "EMBEXPR_END"; + case YP_TOKEN_EMBVAR: + return "EMBVAR"; + case YP_TOKEN_EQUAL: + return "EQUAL"; + case YP_TOKEN_EQUAL_EQUAL: + return "EQUAL_EQUAL"; + case YP_TOKEN_EQUAL_EQUAL_EQUAL: + return "EQUAL_EQUAL_EQUAL"; + case YP_TOKEN_EQUAL_GREATER: + return "EQUAL_GREATER"; + case YP_TOKEN_EQUAL_TILDE: + return "EQUAL_TILDE"; + case YP_TOKEN_FLOAT: + return "FLOAT"; + case YP_TOKEN_GLOBAL_VARIABLE: + return "GLOBAL_VARIABLE"; + case YP_TOKEN_GREATER: + return "GREATER"; + case YP_TOKEN_GREATER_EQUAL: + return "GREATER_EQUAL"; + case YP_TOKEN_GREATER_GREATER: + return "GREATER_GREATER"; + case YP_TOKEN_GREATER_GREATER_EQUAL: + return "GREATER_GREATER_EQUAL"; + case YP_TOKEN_HEREDOC_END: + return "HEREDOC_END"; + case YP_TOKEN_HEREDOC_START: + return "HEREDOC_START"; + case YP_TOKEN_IDENTIFIER: + return "IDENTIFIER"; + case YP_TOKEN_IGNORED_NEWLINE: + return "IGNORED_NEWLINE"; + case YP_TOKEN_IMAGINARY_NUMBER: + return "IMAGINARY_NUMBER"; + case YP_TOKEN_INSTANCE_VARIABLE: + return "INSTANCE_VARIABLE"; + case YP_TOKEN_INTEGER: + return "INTEGER"; + case YP_TOKEN_KEYWORD_ALIAS: + return "KEYWORD_ALIAS"; + case YP_TOKEN_KEYWORD_AND: + return "KEYWORD_AND"; + case YP_TOKEN_KEYWORD_BEGIN: + return "KEYWORD_BEGIN"; + case YP_TOKEN_KEYWORD_BEGIN_UPCASE: + return "KEYWORD_BEGIN_UPCASE"; + case YP_TOKEN_KEYWORD_BREAK: + return "KEYWORD_BREAK"; + case YP_TOKEN_KEYWORD_CASE: + return "KEYWORD_CASE"; + case YP_TOKEN_KEYWORD_CLASS: + return "KEYWORD_CLASS"; + case YP_TOKEN_KEYWORD_DEF: + return "KEYWORD_DEF"; + case YP_TOKEN_KEYWORD_DEFINED: + return "KEYWORD_DEFINED"; + case YP_TOKEN_KEYWORD_DO: + return "KEYWORD_DO"; + case YP_TOKEN_KEYWORD_DO_LOOP: + return "KEYWORD_DO_LOOP"; + case YP_TOKEN_KEYWORD_ELSE: + return "KEYWORD_ELSE"; + case YP_TOKEN_KEYWORD_ELSIF: + return "KEYWORD_ELSIF"; + case YP_TOKEN_KEYWORD_END: + return "KEYWORD_END"; + case YP_TOKEN_KEYWORD_END_UPCASE: + return "KEYWORD_END_UPCASE"; + case YP_TOKEN_KEYWORD_ENSURE: + return "KEYWORD_ENSURE"; + case YP_TOKEN_KEYWORD_FALSE: + return "KEYWORD_FALSE"; + case YP_TOKEN_KEYWORD_FOR: + return "KEYWORD_FOR"; + case YP_TOKEN_KEYWORD_IF: + return "KEYWORD_IF"; + case YP_TOKEN_KEYWORD_IF_MODIFIER: + return "KEYWORD_IF_MODIFIER"; + case YP_TOKEN_KEYWORD_IN: + return "KEYWORD_IN"; + case YP_TOKEN_KEYWORD_MODULE: + return "KEYWORD_MODULE"; + case YP_TOKEN_KEYWORD_NEXT: + return "KEYWORD_NEXT"; + case YP_TOKEN_KEYWORD_NIL: + return "KEYWORD_NIL"; + case YP_TOKEN_KEYWORD_NOT: + return "KEYWORD_NOT"; + case YP_TOKEN_KEYWORD_OR: + return "KEYWORD_OR"; + case YP_TOKEN_KEYWORD_REDO: + return "KEYWORD_REDO"; + case YP_TOKEN_KEYWORD_RESCUE: + return "KEYWORD_RESCUE"; + case YP_TOKEN_KEYWORD_RESCUE_MODIFIER: + return "KEYWORD_RESCUE_MODIFIER"; + case YP_TOKEN_KEYWORD_RETRY: + return "KEYWORD_RETRY"; + case YP_TOKEN_KEYWORD_RETURN: + return "KEYWORD_RETURN"; + case YP_TOKEN_KEYWORD_SELF: + return "KEYWORD_SELF"; + case YP_TOKEN_KEYWORD_SUPER: + return "KEYWORD_SUPER"; + case YP_TOKEN_KEYWORD_THEN: + return "KEYWORD_THEN"; + case YP_TOKEN_KEYWORD_TRUE: + return "KEYWORD_TRUE"; + case YP_TOKEN_KEYWORD_UNDEF: + return "KEYWORD_UNDEF"; + case YP_TOKEN_KEYWORD_UNLESS: + return "KEYWORD_UNLESS"; + case YP_TOKEN_KEYWORD_UNLESS_MODIFIER: + return "KEYWORD_UNLESS_MODIFIER"; + case YP_TOKEN_KEYWORD_UNTIL: + return "KEYWORD_UNTIL"; + case YP_TOKEN_KEYWORD_UNTIL_MODIFIER: + return "KEYWORD_UNTIL_MODIFIER"; + case YP_TOKEN_KEYWORD_WHEN: + return "KEYWORD_WHEN"; + case YP_TOKEN_KEYWORD_WHILE: + return "KEYWORD_WHILE"; + case YP_TOKEN_KEYWORD_WHILE_MODIFIER: + return "KEYWORD_WHILE_MODIFIER"; + case YP_TOKEN_KEYWORD_YIELD: + return "KEYWORD_YIELD"; + case YP_TOKEN_KEYWORD___ENCODING__: + return "KEYWORD___ENCODING__"; + case YP_TOKEN_KEYWORD___FILE__: + return "KEYWORD___FILE__"; + case YP_TOKEN_KEYWORD___LINE__: + return "KEYWORD___LINE__"; + case YP_TOKEN_LABEL: + return "LABEL"; + case YP_TOKEN_LABEL_END: + return "LABEL_END"; + case YP_TOKEN_LAMBDA_BEGIN: + return "LAMBDA_BEGIN"; + case YP_TOKEN_LESS: + return "LESS"; + case YP_TOKEN_LESS_EQUAL: + return "LESS_EQUAL"; + case YP_TOKEN_LESS_EQUAL_GREATER: + return "LESS_EQUAL_GREATER"; + case YP_TOKEN_LESS_LESS: + return "LESS_LESS"; + case YP_TOKEN_LESS_LESS_EQUAL: + return "LESS_LESS_EQUAL"; + case YP_TOKEN_MINUS: + return "MINUS"; + case YP_TOKEN_MINUS_EQUAL: + return "MINUS_EQUAL"; + case YP_TOKEN_MINUS_GREATER: + return "MINUS_GREATER"; + case YP_TOKEN_NEWLINE: + return "NEWLINE"; + case YP_TOKEN_NUMBERED_REFERENCE: + return "NUMBERED_REFERENCE"; + case YP_TOKEN_PARENTHESIS_LEFT: + return "PARENTHESIS_LEFT"; + case YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES: + return "PARENTHESIS_LEFT_PARENTHESES"; + case YP_TOKEN_PARENTHESIS_RIGHT: + return "PARENTHESIS_RIGHT"; + case YP_TOKEN_PERCENT: + return "PERCENT"; + case YP_TOKEN_PERCENT_EQUAL: + return "PERCENT_EQUAL"; + case YP_TOKEN_PERCENT_LOWER_I: + return "PERCENT_LOWER_I"; + case YP_TOKEN_PERCENT_LOWER_W: + return "PERCENT_LOWER_W"; + case YP_TOKEN_PERCENT_LOWER_X: + return "PERCENT_LOWER_X"; + case YP_TOKEN_PERCENT_UPPER_I: + return "PERCENT_UPPER_I"; + case YP_TOKEN_PERCENT_UPPER_W: + return "PERCENT_UPPER_W"; + case YP_TOKEN_PIPE: + return "PIPE"; + case YP_TOKEN_PIPE_EQUAL: + return "PIPE_EQUAL"; + case YP_TOKEN_PIPE_PIPE: + return "PIPE_PIPE"; + case YP_TOKEN_PIPE_PIPE_EQUAL: + return "PIPE_PIPE_EQUAL"; + case YP_TOKEN_PLUS: + return "PLUS"; + case YP_TOKEN_PLUS_EQUAL: + return "PLUS_EQUAL"; + case YP_TOKEN_QUESTION_MARK: + return "QUESTION_MARK"; + case YP_TOKEN_RATIONAL_NUMBER: + return "RATIONAL_NUMBER"; + case YP_TOKEN_REGEXP_BEGIN: + return "REGEXP_BEGIN"; + case YP_TOKEN_REGEXP_END: + return "REGEXP_END"; + case YP_TOKEN_SEMICOLON: + return "SEMICOLON"; + case YP_TOKEN_SLASH: + return "SLASH"; + case YP_TOKEN_SLASH_EQUAL: + return "SLASH_EQUAL"; + case YP_TOKEN_STAR: + return "STAR"; + case YP_TOKEN_STAR_EQUAL: + return "STAR_EQUAL"; + case YP_TOKEN_STAR_STAR: + return "STAR_STAR"; + case YP_TOKEN_STAR_STAR_EQUAL: + return "STAR_STAR_EQUAL"; + case YP_TOKEN_STRING_BEGIN: + return "STRING_BEGIN"; + case YP_TOKEN_STRING_CONTENT: + return "STRING_CONTENT"; + case YP_TOKEN_STRING_END: + return "STRING_END"; + case YP_TOKEN_SYMBOL_BEGIN: + return "SYMBOL_BEGIN"; + case YP_TOKEN_TILDE: + return "TILDE"; + case YP_TOKEN_UCOLON_COLON: + return "UCOLON_COLON"; + case YP_TOKEN_UDOT_DOT: + return "UDOT_DOT"; + case YP_TOKEN_UDOT_DOT_DOT: + return "UDOT_DOT_DOT"; + case YP_TOKEN_UMINUS: + return "UMINUS"; + case YP_TOKEN_UMINUS_NUM: + return "UMINUS_NUM"; + case YP_TOKEN_UPLUS: + return "UPLUS"; + case YP_TOKEN_USTAR: + return "USTAR"; + case YP_TOKEN_USTAR_STAR: + return "USTAR_STAR"; + case YP_TOKEN_WORDS_SEP: + return "WORDS_SEP"; + case YP_TOKEN___END__: + return "__END__"; + case YP_TOKEN_MAXIMUM: + return "MAXIMUM"; + } + return "\0"; +} diff --git a/src/main/c/yarp/src/unescape.c b/src/main/c/yarp/src/unescape.c new file mode 100644 index 000000000000..5370b2735579 --- /dev/null +++ b/src/main/c/yarp/src/unescape.c @@ -0,0 +1,559 @@ +#include "yarp/unescape.h" + +/******************************************************************************/ +/* Character checks */ +/******************************************************************************/ + +static inline bool +yp_char_is_hexadecimal_digits(const char *c, size_t length) { + for (size_t index = 0; index < length; index++) { + if (!yp_char_is_hexadecimal_digit(c[index])) { + return false; + } + } + return true; +} + +/******************************************************************************/ +/* Lookup tables for characters */ +/******************************************************************************/ + +// This is a lookup table for unescapes that only take up a single character. +static const unsigned char unescape_chars[] = { + ['\''] = '\'', + ['\\'] = '\\', + ['a'] = '\a', + ['b'] = '\b', + ['e'] = '\033', + ['f'] = '\f', + ['n'] = '\n', + ['r'] = '\r', + ['s'] = ' ', + ['t'] = '\t', + ['v'] = '\v' +}; + +// This is a lookup table for whether or not an ASCII character is printable. +static const bool ascii_printable_chars[] = { + 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, + 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 +}; + +static inline bool +char_is_ascii_printable(const char c) { + unsigned char v = (unsigned char) c; + return (v < 0x80) && ascii_printable_chars[v]; +} + +/******************************************************************************/ +/* Unescaping for segments */ +/******************************************************************************/ + +// Scan the 1-3 digits of octal into the value. Returns the number of digits +// scanned. +static inline size_t +unescape_octal(const char *backslash, unsigned char *value) { + *value = (unsigned char) (backslash[1] - '0'); + if (!yp_char_is_octal_digit(backslash[2])) { + return 2; + } + + *value = (*value << 3) | (backslash[2] - '0'); + if (!yp_char_is_octal_digit(backslash[3])) { + return 3; + } + + *value = (*value << 3) | (backslash[3] - '0'); + return 4; +} + +// Convert a hexadecimal digit into its equivalent value. +static inline unsigned char +unescape_hexadecimal_digit(const char value) { + return (value <= '9') ? (unsigned char) (value - '0') : (value & 0x7) + 9; +} + +// Scan the 1-2 digits of hexadecimal into the value. Returns the number of +// digits scanned. +static inline size_t +unescape_hexadecimal(const char *backslash, unsigned char *value) { + *value = unescape_hexadecimal_digit(backslash[2]); + if (!yp_char_is_hexadecimal_digit(backslash[3])) { + return 3; + } + + *value = (*value << 4) | unescape_hexadecimal_digit(backslash[3]); + return 4; +} + +// Scan the 4 digits of a Unicode escape into the value. Returns the number of +// digits scanned. This function assumes that the characters have already been +// validated. +static inline void +unescape_unicode(const char *string, size_t length, uint32_t *value) { + *value = 0; + for (size_t index = 0; index < length; index++) { + if (index != 0) *value <<= 4; + *value |= unescape_hexadecimal_digit(string[index]); + } +} + +// Accepts the pointer to the string to write the unicode value along with the +// 32-bit value to write. Writes the UTF-8 representation of the value to the +// string and returns the number of bytes written. +static inline size_t +unescape_unicode_write(char *dest, uint32_t value, const char *start, const char *end, yp_list_t *error_list) { + unsigned char *bytes = (unsigned char *) dest; + + if (value <= 0x7F) { + // 0xxxxxxx + bytes[0] = value; + return 1; + } + + if (value <= 0x7FF) { + // 110xxxxx 10xxxxxx + bytes[0] = 0xC0 | (value >> 6); + bytes[1] = 0x80 | (value & 0x3F); + return 2; + } + + if (value <= 0xFFFF) { + // 1110xxxx 10xxxxxx 10xxxxxx + bytes[0] = 0xE0 | (value >> 12); + bytes[1] = 0x80 | ((value >> 6) & 0x3F); + bytes[2] = 0x80 | (value & 0x3F); + return 3; + } + + // At this point it must be a 4 digit UTF-8 representation. If it's not, then + // the input is invalid. + if (value <= 0x10FFFF) { + // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx + bytes[0] = 0xF0 | (value >> 18); + bytes[1] = 0x80 | ((value >> 12) & 0x3F); + bytes[2] = 0x80 | ((value >> 6) & 0x3F); + bytes[3] = 0x80 | (value & 0x3F); + return 4; + } + + // If we get here, then the value is too big. This is an error, but we don't + // want to just crash, so instead we'll add an error to the error list and put + // in a replacement character instead. + yp_diagnostic_list_append(error_list, start, end, "Invalid Unicode escape sequence."); + bytes[0] = 0xEF; + bytes[1] = 0xBF; + bytes[2] = 0xBD; + return 3; +} + +typedef enum { + YP_UNESCAPE_FLAG_NONE = 0, + YP_UNESCAPE_FLAG_CONTROL = 1, + YP_UNESCAPE_FLAG_META = 2, + YP_UNESCAPE_FLAG_EXPECT_SINGLE = 4 +} yp_unescape_flag_t; + +// Unescape a single character value based on the given flags. +static inline unsigned char +unescape_char(const unsigned char value, const unsigned char flags) { + unsigned char unescaped = value; + + if (flags & YP_UNESCAPE_FLAG_CONTROL) { + unescaped &= 0x1f; + } + + if (flags & YP_UNESCAPE_FLAG_META) { + unescaped |= 0x80; + } + + return unescaped; +} + +// Read a specific escape sequence into the given destination. +static const char * +unescape(char *dest, size_t *dest_length, const char *backslash, const char *end, yp_list_t *error_list, const unsigned char flags, bool write_to_str) { + switch (backslash[1]) { + // \a \b \e \f \n \r \s \t \v + case '\r': { + // if this is an \r\n we need to escape both + if (write_to_str) { + dest[(*dest_length)++] = (char) unescape_char(unescape_chars[(unsigned char) backslash[1]], flags); + } + + if (backslash + 2 < end && backslash[2] == '\n') { + if (write_to_str) { + dest[(*dest_length)++] = (char) unescape_char(unescape_chars[(unsigned char) backslash[2]], flags); + } + return backslash + 3; + } + + return backslash + 2; + } + case 'a': + case 'b': + case 'e': + case 'f': + case 'n': + case 'r': + case 's': + case 't': + case 'v': + if (write_to_str) { + dest[(*dest_length)++] = (char) unescape_char(unescape_chars[(unsigned char) backslash[1]], flags); + } + return backslash + 2; + // \nnn octal bit pattern, where nnn is 1-3 octal digits ([0-7]) + case '0': case '1': case '2': case '3': case '4': + case '5': case '6': case '7': case '8': case '9': { + unsigned char value; + const char *cursor = backslash + unescape_octal(backslash, &value); + + if (write_to_str) { + dest[(*dest_length)++] = (char) unescape_char(value, flags); + } + return cursor; + } + // \xnn hexadecimal bit pattern, where nn is 1-2 hexadecimal digits ([0-9a-fA-F]) + case 'x': { + unsigned char value; + const char *cursor = backslash + unescape_hexadecimal(backslash, &value); + + if (write_to_str) { + dest[(*dest_length)++] = (char) unescape_char(value, flags); + } + return cursor; + } + // \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F]) + // \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F]) + case 'u': { + if ((flags & YP_UNESCAPE_FLAG_CONTROL) | (flags & YP_UNESCAPE_FLAG_META)) { + yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Unicode escape sequence cannot be used with control or meta flags."); + return backslash + 2; + } + + if ((backslash + 3) < end && backslash[2] == '{') { + const char *unicode_cursor = backslash + 3; + const char *extra_codepoints_start = NULL; + int codepoints_count = 0; + + unicode_cursor += yp_strspn_whitespace(unicode_cursor, end - unicode_cursor); + + while ((*unicode_cursor != '}') && (unicode_cursor < end)) { + const char *unicode_start = unicode_cursor; + size_t hexadecimal_length = yp_strspn_hexadecimal_digit(unicode_cursor, end - unicode_cursor); + + // \u{nnnn} character literal allows only 1-6 hexadecimal digits + if (hexadecimal_length > 6) + yp_diagnostic_list_append(error_list, unicode_cursor, unicode_cursor + hexadecimal_length, "invalid Unicode escape."); + + // there are not hexadecimal characters + if (hexadecimal_length == 0) { + yp_diagnostic_list_append(error_list, unicode_cursor, unicode_cursor + hexadecimal_length, "unterminated Unicode escape"); + return unicode_cursor; + } + + unicode_cursor += hexadecimal_length; + + codepoints_count++; + if (flags & YP_UNESCAPE_FLAG_EXPECT_SINGLE && codepoints_count == 2) + extra_codepoints_start = unicode_start; + + uint32_t value; + unescape_unicode(unicode_start, (size_t) (unicode_cursor - unicode_start), &value); + if (write_to_str) { + *dest_length += unescape_unicode_write(dest + *dest_length, value, unicode_start, unicode_cursor, error_list); + } + + unicode_cursor += yp_strspn_whitespace(unicode_cursor, end - unicode_cursor); + } + + // ?\u{nnnn} character literal should contain only one codepoint and cannot be like ?\u{nnnn mmmm} + if (flags & YP_UNESCAPE_FLAG_EXPECT_SINGLE && codepoints_count > 1) + yp_diagnostic_list_append(error_list, extra_codepoints_start, unicode_cursor - 1, "Multiple codepoints at single character literal"); + + return unicode_cursor + 1; + } + + if ((backslash + 2) < end && yp_char_is_hexadecimal_digits(backslash + 2, 4)) { + uint32_t value; + unescape_unicode(backslash + 2, 4, &value); + + if (write_to_str) { + *dest_length += unescape_unicode_write(dest + *dest_length, value, backslash + 2, backslash + 6, error_list); + } + return backslash + 6; + } + + yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Invalid Unicode escape sequence"); + return backslash + 2; + } + // \c\M-x meta control character, where x is an ASCII printable character + // \c? delete, ASCII 7Fh (DEL) + // \cx control character, where x is an ASCII printable character + case 'c': + if (backslash + 2 >= end) { + yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Invalid control escape sequence"); + return end; + } + + if (flags & YP_UNESCAPE_FLAG_CONTROL) { + yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Control escape sequence cannot be doubled."); + return backslash + 2; + } + + switch (backslash[2]) { + case '\\': + return unescape(dest, dest_length, backslash + 2, end, error_list, flags | YP_UNESCAPE_FLAG_CONTROL, write_to_str); + case '?': + if (write_to_str) { + dest[(*dest_length)++] = (char) unescape_char(0x7f, flags); + } + return backslash + 3; + default: { + if (!char_is_ascii_printable(backslash[2])) { + yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Invalid control escape sequence"); + return backslash + 2; + } + + if (write_to_str) { + dest[(*dest_length)++] = (char) unescape_char((const unsigned char) backslash[2], flags | YP_UNESCAPE_FLAG_CONTROL); + } + return backslash + 3; + } + } + // \C-x control character, where x is an ASCII printable character + // \C-? delete, ASCII 7Fh (DEL) + case 'C': + if (backslash + 3 >= end) { + yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Invalid control escape sequence"); + return end; + } + + if (flags & YP_UNESCAPE_FLAG_CONTROL) { + yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Control escape sequence cannot be doubled."); + return backslash + 2; + } + + if (backslash[2] != '-') { + yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Invalid control escape sequence"); + return backslash + 2; + } + + switch (backslash[3]) { + case '\\': + return unescape(dest, dest_length, backslash + 3, end, error_list, flags | YP_UNESCAPE_FLAG_CONTROL, write_to_str); + case '?': + if (write_to_str) { + dest[(*dest_length)++] = (char) unescape_char(0x7f, flags); + } + return backslash + 4; + default: + if (!char_is_ascii_printable(backslash[3])) { + yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Invalid control escape sequence"); + return backslash + 2; + } + + if (write_to_str) { + dest[(*dest_length)++] = (char) unescape_char((const unsigned char) backslash[3], flags | YP_UNESCAPE_FLAG_CONTROL); + } + return backslash + 4; + } + // \M-\C-x meta control character, where x is an ASCII printable character + // \M-\cx meta control character, where x is an ASCII printable character + // \M-x meta character, where x is an ASCII printable character + case 'M': { + if (backslash + 3 >= end) { + yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Invalid control escape sequence"); + return end; + } + + if (flags & YP_UNESCAPE_FLAG_META) { + yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Meta escape sequence cannot be doubled."); + return backslash + 2; + } + + if (backslash[2] != '-') { + yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Invalid meta escape sequence"); + return backslash + 2; + } + + if (backslash[3] == '\\') { + return unescape(dest, dest_length, backslash + 3, end, error_list, flags | YP_UNESCAPE_FLAG_META, write_to_str); + } + + if (char_is_ascii_printable(backslash[3])) { + if (write_to_str) { + dest[(*dest_length)++] = (char) unescape_char((const unsigned char) backslash[3], flags | YP_UNESCAPE_FLAG_META); + } + return backslash + 4; + } + + yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Invalid meta escape sequence"); + return backslash + 3; + } + // In this case we're escaping something that doesn't need escaping. + default: + { + if (write_to_str) { + dest[(*dest_length)++] = backslash[1]; + } + return backslash + 2; + } + } +} + +/******************************************************************************/ +/* Public functions and entrypoints */ +/******************************************************************************/ + +// Unescape the contents of the given token into the given string using the +// given unescape mode. The supported escapes are: +// +// \a bell, ASCII 07h (BEL) +// \b backspace, ASCII 08h (BS) +// \t horizontal tab, ASCII 09h (TAB) +// \n newline (line feed), ASCII 0Ah (LF) +// \v vertical tab, ASCII 0Bh (VT) +// \f form feed, ASCII 0Ch (FF) +// \r carriage return, ASCII 0Dh (CR) +// \e escape, ASCII 1Bh (ESC) +// \s space, ASCII 20h (SPC) +// \\ backslash +// \nnn octal bit pattern, where nnn is 1-3 octal digits ([0-7]) +// \xnn hexadecimal bit pattern, where nn is 1-2 hexadecimal digits ([0-9a-fA-F]) +// \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F]) +// \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F]) +// \cx or \C-x control character, where x is an ASCII printable character +// \M-x meta character, where x is an ASCII printable character +// \M-\C-x meta control character, where x is an ASCII printable character +// \M-\cx same as above +// \c\M-x same as above +// \c? or \C-? delete, ASCII 7Fh (DEL) +// +YP_EXPORTED_FUNCTION void +yp_unescape_manipulate_string(yp_parser_t *parser, const char *value, size_t length, yp_string_t *string, yp_unescape_type_t unescape_type, yp_list_t *error_list) { + if (unescape_type == YP_UNESCAPE_NONE) { + // If we're not unescaping then we can reference the source directly. + yp_string_shared_init(string, value, value + length); + return; + } + + const char *backslash = yp_memchr(parser, value, '\\', length); + + if (backslash == NULL) { + // Here there are no escapes, so we can reference the source directly. + yp_string_shared_init(string, value, value + length); + return; + } + + // Here we have found an escape character, so we need to handle all escapes + // within the string. + char *allocated = malloc(length); + if (allocated == NULL) { + yp_diagnostic_list_append(error_list, value, value + length, "Failed to allocate memory for unescaping."); + return; + } + + yp_string_owned_init(string, allocated, length); + + // This is the memory address where we're putting the unescaped string. + char *dest = string->as.owned.source; + size_t dest_length = 0; + + // This is the current position in the source string that we're looking at. + // It's going to move along behind the backslash so that we can copy each + // segment of the string that doesn't contain an escape. + const char *cursor = value; + const char *end = value + length; + + // For each escape found in the source string, we will handle it and update + // the moving cursor->backslash window. + while (backslash != NULL && backslash + 1 < end) { + assert(dest_length < length); + + // This is the size of the segment of the string from the previous escape + // or the start of the string to the current escape. + size_t segment_size = (size_t) (backslash - cursor); + + // Here we're going to copy everything up until the escape into the + // destination buffer. + memcpy(dest + dest_length, cursor, segment_size); + dest_length += segment_size; + + switch (backslash[1]) { + case '\\': + case '\'': + dest[dest_length++] = (char) unescape_chars[(unsigned char) backslash[1]]; + cursor = backslash + 2; + break; + default: + if (unescape_type == YP_UNESCAPE_MINIMAL) { + // In this case we're escaping something that doesn't need escaping. + dest[dest_length++] = '\\'; + cursor = backslash + 1; + break; + } + + // This is the only type of unescaping left. In this case we need to + // handle all of the different unescapes. + assert(unescape_type == YP_UNESCAPE_ALL); + cursor = unescape(dest, &dest_length, backslash, end, error_list, YP_UNESCAPE_FLAG_NONE, true); + break; + } + + if (end > cursor) { + backslash = yp_memchr(parser, cursor, '\\', (size_t) (end - cursor)); + } else { + backslash = NULL; + } + } + + // We need to copy the final segment of the string after the last escape. + if (end > cursor) { + memcpy(dest + dest_length, cursor, (size_t) (end - cursor)); + } else { + cursor = end; + } + + // We also need to update the length at the end. This is because every escape + // reduces the length of the final string, and we don't want garbage at the + // end. + string->as.owned.length = dest_length + ((size_t) (end - cursor)); +} + +// This function is similar to yp_unescape_manipulate_string, except it doesn't +// actually perform any string manipulations. Instead, it calculates how long +// the unescaped character is, and returns that value +YP_EXPORTED_FUNCTION size_t +yp_unescape_calculate_difference(const char *backslash, const char *end, yp_unescape_type_t unescape_type, bool expect_single_codepoint, yp_list_t *error_list) { + assert(unescape_type != YP_UNESCAPE_NONE); + + switch (backslash[1]) { + case '\\': + case '\'': + return 2; + default: { + if (unescape_type == YP_UNESCAPE_MINIMAL) return 2; + + // This is the only type of unescaping left. In this case we need to + // handle all of the different unescapes. + assert(unescape_type == YP_UNESCAPE_ALL); + + unsigned char flags = YP_UNESCAPE_FLAG_NONE; + if (expect_single_codepoint) + flags |= YP_UNESCAPE_FLAG_EXPECT_SINGLE; + + const char *cursor = unescape(NULL, 0, backslash, end, error_list, flags, false); + assert(cursor > backslash); + + return (size_t) (cursor - backslash); + } + } +} diff --git a/src/main/c/yarp/src/util/yp_buffer.c b/src/main/c/yarp/src/util/yp_buffer.c new file mode 100644 index 000000000000..df33904039c9 --- /dev/null +++ b/src/main/c/yarp/src/util/yp_buffer.c @@ -0,0 +1,78 @@ +#include "yarp/util/yp_buffer.h" + +#define YP_BUFFER_INITIAL_SIZE 1024 + +// Initialize a yp_buffer_t with its default values. +bool +yp_buffer_init(yp_buffer_t *buffer) { + buffer->length = 0; + buffer->capacity = YP_BUFFER_INITIAL_SIZE; + + buffer->value = (char *) malloc(YP_BUFFER_INITIAL_SIZE); + return buffer->value != NULL; +} + +// Append the given amount of space to the buffer. +static inline void +yp_buffer_append_length(yp_buffer_t *buffer, size_t length) { + size_t next_length = buffer->length + length; + + if (next_length > buffer->capacity) { + do { + buffer->capacity *= 2; + } while (next_length > buffer->capacity); + + buffer->value = realloc(buffer->value, buffer->capacity); + } + + buffer->length = next_length; +} + +// Append a generic pointer to memory to the buffer. +static inline void +yp_buffer_append(yp_buffer_t *buffer, const void *source, size_t length) { + yp_buffer_append_length(buffer, length); + memcpy(buffer->value + (buffer->length - length), source, length); +} + +// Append the given amount of space as zeroes to the buffer. +void +yp_buffer_append_zeroes(yp_buffer_t *buffer, size_t length) { + yp_buffer_append_length(buffer, length); + memset(buffer->value + (buffer->length - length), 0, length); +} + +// Append a string to the buffer. +void +yp_buffer_append_str(yp_buffer_t *buffer, const char *value, size_t length) { + const void *source = value; + yp_buffer_append(buffer, source, length); +} + +// Append a single byte to the buffer. +void +yp_buffer_append_u8(yp_buffer_t *buffer, uint8_t value) { + const void *source = &value; + yp_buffer_append(buffer, source, sizeof(uint8_t)); +} + +// Append a 32-bit unsigned integer to the buffer. +void +yp_buffer_append_u32(yp_buffer_t *buffer, uint32_t value) { + if (value < 128) { + yp_buffer_append_u8(buffer, (uint8_t) value); + } else { + uint32_t n = value; + while (n >= 128) { + yp_buffer_append_u8(buffer, (uint8_t) (n | 128)); + n >>= 7; + } + yp_buffer_append_u8(buffer, (uint8_t) n); + } +} + +// Free the memory associated with the buffer. +void +yp_buffer_free(yp_buffer_t *buffer) { + free(buffer->value); +} diff --git a/src/main/c/yarp/src/util/yp_char.c b/src/main/c/yarp/src/util/yp_char.c new file mode 100644 index 000000000000..9befcb51053b --- /dev/null +++ b/src/main/c/yarp/src/util/yp_char.c @@ -0,0 +1,224 @@ +#include "yarp/util/yp_char.h" + +#define YP_CHAR_BIT_WHITESPACE (1 << 0) +#define YP_CHAR_BIT_INLINE_WHITESPACE (1 << 1) +#define YP_CHAR_BIT_REGEXP_OPTION (1 << 2) + +#define YP_NUMBER_BIT_BINARY_DIGIT (1 << 0) +#define YP_NUMBER_BIT_BINARY_NUMBER (1 << 1) +#define YP_NUMBER_BIT_OCTAL_DIGIT (1 << 2) +#define YP_NUMBER_BIT_OCTAL_NUMBER (1 << 3) +#define YP_NUMBER_BIT_DECIMAL_DIGIT (1 << 4) +#define YP_NUMBER_BIT_DECIMAL_NUMBER (1 << 5) +#define YP_NUMBER_BIT_HEXADECIMAL_DIGIT (1 << 6) +#define YP_NUMBER_BIT_HEXADECIMAL_NUMBER (1 << 7) + +static const unsigned char yp_char_table[256] = { +//0 1 2 3 4 5 6 7 8 9 A B C D E F + 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 3, 3, 3, 0, 0, // 0x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x + 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5x + 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 4, 4, // 6x + 0, 0, 0, 4, 0, 4, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, // 7x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx +}; + +static const unsigned char yp_number_table[256] = { + // 0 1 2 3 4 5 6 7 8 9 A B C D E F + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2x + 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3x + 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, // 5x + 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9x + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ax + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Bx + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Cx + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dx + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ex + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Fx +}; + +static inline size_t +yp_strspn_char_kind(const char *string, ptrdiff_t length, unsigned char kind) { + if (length <= 0) return 0; + + size_t size = 0; + size_t maximum = (size_t) length; + + while (size < maximum && (yp_char_table[(unsigned char) string[size]] & kind)) size++; + return size; +} + +// Returns the number of characters at the start of the string that are +// whitespace. Disallows searching past the given maximum number of characters. +size_t +yp_strspn_whitespace(const char *string, ptrdiff_t length) { + return yp_strspn_char_kind(string, length, YP_CHAR_BIT_WHITESPACE); +} + +// Returns the number of characters at the start of the string that are +// whitespace while also tracking the location of each newline. Disallows +// searching past the given maximum number of characters. +size_t +yp_strspn_whitespace_newlines(const char *string, long length, yp_newline_list_t *newline_list) { + if (length <= 0) return 0; + + size_t size = 0; + size_t maximum = (size_t) length; + + while (size < maximum && (yp_char_table[(unsigned char) string[size]] & YP_CHAR_BIT_WHITESPACE)) { + if (string[size] == '\n') { + yp_newline_list_append(newline_list, string + size); + } + + size++; + } + + return size; +} + +// Returns the number of characters at the start of the string that are inline +// whitespace. Disallows searching past the given maximum number of characters. +size_t +yp_strspn_inline_whitespace(const char *string, ptrdiff_t length) { + return yp_strspn_char_kind(string, length, YP_CHAR_BIT_INLINE_WHITESPACE); +} + +// Returns the number of characters at the start of the string that are regexp +// options. Disallows searching past the given maximum number of characters. +size_t +yp_strspn_regexp_option(const char *string, ptrdiff_t length) { + return yp_strspn_char_kind(string, length, YP_CHAR_BIT_REGEXP_OPTION); +} + +static inline bool +yp_char_is_char_kind(const char c, unsigned char kind) { + return (yp_char_table[(unsigned char) c] & kind) != 0; +} + +// Returns true if the given character is a whitespace character. +bool +yp_char_is_whitespace(const char c) { + return yp_char_is_char_kind(c, YP_CHAR_BIT_WHITESPACE); +} + +// Returns true if the given character is an inline whitespace character. +bool +yp_char_is_inline_whitespace(const char c) { + return yp_char_is_char_kind(c, YP_CHAR_BIT_INLINE_WHITESPACE); +} + +static inline size_t +yp_strspn_number_kind(const char *string, ptrdiff_t length, unsigned char kind) { + if (length <= 0) return 0; + + size_t size = 0; + size_t maximum = (size_t) length; + + while (size < maximum && (yp_number_table[(unsigned char) string[size]] & kind)) size++; + return size; +} + +// Returns the number of characters at the start of the string that are binary +// digits or underscores. Disallows searching past the given maximum number of +// characters. +size_t +yp_strspn_binary_number(const char *string, ptrdiff_t length) { + return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_BINARY_NUMBER); +} + +// Returns the number of characters at the start of the string that are octal +// digits or underscores. Disallows searching past the given maximum number of +// characters. +size_t +yp_strspn_octal_number(const char *string, ptrdiff_t length) { + return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_OCTAL_NUMBER); +} + +// Returns the number of characters at the start of the string that are decimal +// digits. Disallows searching past the given maximum number of characters. +size_t +yp_strspn_decimal_digit(const char *string, ptrdiff_t length) { + return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_DECIMAL_DIGIT); +} + +// Returns the number of characters at the start of the string that are decimal +// digits or underscores. Disallows searching past the given maximum number of +// characters. +size_t +yp_strspn_decimal_number(const char *string, ptrdiff_t length) { + return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_DECIMAL_NUMBER); +} + +// Returns the number of characters at the start of the string that are +// hexadecimal digits. Disallows searching past the given maximum number of +// characters. +size_t +yp_strspn_hexadecimal_digit(const char *string, ptrdiff_t length) { + return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_HEXADECIMAL_DIGIT); +} + +// Returns the number of characters at the start of the string that are +// hexadecimal digits or underscores. Disallows searching past the given maximum +// number of characters. +size_t +yp_strspn_hexadecimal_number(const char *string, ptrdiff_t length) { + return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_HEXADECIMAL_NUMBER); +} + +static inline bool +yp_char_is_number_kind(const char c, unsigned char kind) { + return (yp_number_table[(unsigned char) c] & kind) != 0; +} + +// Returns true if the given character is a binary digit. +bool +yp_char_is_binary_digit(const char c) { + return yp_char_is_number_kind(c, YP_NUMBER_BIT_BINARY_DIGIT); +} + +// Returns true if the given character is an octal digit. +bool +yp_char_is_octal_digit(const char c) { + return yp_char_is_number_kind(c, YP_NUMBER_BIT_OCTAL_DIGIT); +} + +// Returns true if the given character is a decimal digit. +bool +yp_char_is_decimal_digit(const char c) { + return yp_char_is_number_kind(c, YP_NUMBER_BIT_DECIMAL_DIGIT); +} + +// Returns true if the given character is a hexadecimal digit. +bool +yp_char_is_hexadecimal_digit(const char c) { + return yp_char_is_number_kind(c, YP_NUMBER_BIT_HEXADECIMAL_DIGIT); +} + +#undef YP_CHAR_BIT_WHITESPACE +#undef YP_CHAR_BIT_INLINE_WHITESPACE +#undef YP_CHAR_BIT_REGEXP_OPTION + +#undef YP_NUMBER_BIT_BINARY_DIGIT +#undef YP_NUMBER_BIT_BINARY_NUMBER +#undef YP_NUMBER_BIT_OCTAL_DIGIT +#undef YP_NUMBER_BIT_OCTAL_NUMBER +#undef YP_NUMBER_BIT_DECIMAL_DIGIT +#undef YP_NUMBER_BIT_DECIMAL_NUMBER +#undef YP_NUMBER_BIT_HEXADECIMAL_NUMBER +#undef YP_NUMBER_BIT_HEXADECIMAL_DIGIT diff --git a/src/main/c/yarp/src/util/yp_constant_pool.c b/src/main/c/yarp/src/util/yp_constant_pool.c new file mode 100644 index 000000000000..8c1889c6b41c --- /dev/null +++ b/src/main/c/yarp/src/util/yp_constant_pool.c @@ -0,0 +1,147 @@ +#include "yarp/util/yp_constant_pool.h" + +// Initialize a list of constant ids. +void +yp_constant_id_list_init(yp_constant_id_list_t *list) { + list->ids = NULL; + list->size = 0; + list->capacity = 0; +} + +// Append a constant id to a list of constant ids. Returns false if any +// potential reallocations fail. +bool +yp_constant_id_list_append(yp_constant_id_list_t *list, yp_constant_id_t id) { + if (list->size >= list->capacity) { + list->capacity = list->capacity == 0 ? 8 : list->capacity * 2; + list->ids = (yp_constant_id_t *) realloc(list->ids, sizeof(yp_constant_id_t) * list->capacity); + if (list->ids == NULL) return false; + } + + list->ids[list->size++] = id; + return true; +} + +// Checks if the current constant id list includes the given constant id. +bool +yp_constant_id_list_includes(yp_constant_id_list_t *list, yp_constant_id_t id) { + for (size_t index = 0; index < list->size; index++) { + if (list->ids[index] == id) return true; + } + return false; +} + +// Get the memory size of a list of constant ids. +size_t +yp_constant_id_list_memsize(yp_constant_id_list_t *list) { + return sizeof(yp_constant_id_list_t) + (list->capacity * sizeof(yp_constant_id_t)); +} + +// Free the memory associated with a list of constant ids. +void +yp_constant_id_list_free(yp_constant_id_list_t *list) { + if (list->ids != NULL) { + free(list->ids); + } +} + +// A relatively simple hash function (djb2) that is used to hash strings. We are +// optimizing here for simplicity and speed. +static inline size_t +yp_constant_pool_hash(const char *start, size_t length) { + // This is a prime number used as the initial value for the hash function. + size_t value = 5381; + + for (size_t index = 0; index < length; index++) { + value = ((value << 5) + value) + ((unsigned char) start[index]); + } + + return value; +} + +// Resize a constant pool to a given capacity. +static inline bool +yp_constant_pool_resize(yp_constant_pool_t *pool) { + size_t next_capacity = pool->capacity * 2; + yp_constant_t *next_constants = calloc(next_capacity, sizeof(yp_constant_t)); + if (next_constants == NULL) return false; + + // For each constant in the current constant pool, rehash the content, find + // the index in the next constant pool, and insert it. + for (size_t index = 0; index < pool->capacity; index++) { + yp_constant_t *constant = &pool->constants[index]; + + // If an id is set on this constant, then we know we have content here. + // In this case we need to insert it into the next constant pool. + if (constant->id != 0) { + size_t next_index = constant->hash % next_capacity; + + // This implements linear scanning to find the next available slot + // in case this index is already taken. We don't need to bother + // comparing the values since we know that the hash is unique. + while (next_constants[next_index].id != 0) { + next_index = (next_index + 1) % next_capacity; + } + + // Here we copy over the entire constant, which includes the id so + // that they are consistent between resizes. + next_constants[next_index] = *constant; + } + } + + free(pool->constants); + pool->constants = next_constants; + pool->capacity = next_capacity; + return true; +} + +// Initialize a new constant pool with a given capacity. +bool +yp_constant_pool_init(yp_constant_pool_t *pool, size_t capacity) { + pool->constants = calloc(capacity, sizeof(yp_constant_t)); + if (pool->constants == NULL) return false; + + pool->size = 0; + pool->capacity = capacity; + return true; +} + +// Insert a constant into a constant pool. Returns the id of the constant, or 0 +// if any potential calls to resize fail. +yp_constant_id_t +yp_constant_pool_insert(yp_constant_pool_t *pool, const char *start, size_t length) { + if (pool->size >= pool->capacity * 0.75) { + if (!yp_constant_pool_resize(pool)) return 0; + } + + size_t hash = yp_constant_pool_hash(start, length); + size_t index = hash % pool->capacity; + yp_constant_t *constant; + + while (constant = &pool->constants[index], constant->id != 0) { + // If there is a collision, then we need to check if the content is the + // same as the content we are trying to insert. If it is, then we can + // return the id of the existing constant. + if ((constant->length == length) && strncmp(constant->start, start, length) == 0) { + return pool->constants[index].id; + } + + index = (index + 1) % pool->capacity; + } + + yp_constant_id_t id = (yp_constant_id_t)++pool->size; + pool->constants[index] = (yp_constant_t) { + .id = id, + .start = start, + .length = length, + .hash = hash + }; + + return id; +} + +// Free the memory associated with a constant pool. +void +yp_constant_pool_free(yp_constant_pool_t *pool) { + free(pool->constants); +} diff --git a/src/main/c/yarp/src/util/yp_list.c b/src/main/c/yarp/src/util/yp_list.c new file mode 100644 index 000000000000..dcb072b7d143 --- /dev/null +++ b/src/main/c/yarp/src/util/yp_list.c @@ -0,0 +1,37 @@ +#include "yarp/util/yp_list.h" + +// Initializes a new list. +YP_EXPORTED_FUNCTION void +yp_list_init(yp_list_t *list) { + *list = (yp_list_t) { .head = NULL, .tail = NULL }; +} + +// Returns true if the given list is empty. +YP_EXPORTED_FUNCTION bool +yp_list_empty_p(yp_list_t *list) { + return list->head == NULL; +} + +// Append a node to the given list. +void +yp_list_append(yp_list_t *list, yp_list_node_t *node) { + if (list->head == NULL) { + list->head = node; + } else { + list->tail->next = node; + } + list->tail = node; +} + +// Deallocate the internal state of the given list. +YP_EXPORTED_FUNCTION void +yp_list_free(yp_list_t *list) { + yp_list_node_t *node = list->head; + yp_list_node_t *next; + + while (node != NULL) { + next = node->next; + free(node); + node = next; + } +} diff --git a/src/main/c/yarp/src/util/yp_memchr.c b/src/main/c/yarp/src/util/yp_memchr.c new file mode 100644 index 000000000000..3f63a2cfabe7 --- /dev/null +++ b/src/main/c/yarp/src/util/yp_memchr.c @@ -0,0 +1,31 @@ +#include "yarp/util/yp_memchr.h" + +#define YP_MEMCHR_TRAILING_BYTE_MINIMUM 0x40 + +// We need to roll our own memchr to handle cases where the encoding changes and +// we need to search for a character in a buffer that could be the trailing byte +// of a multibyte character. +void * +yp_memchr(yp_parser_t *parser, const void *memory, int character, size_t number) { + if (parser->encoding_changed && parser->encoding.multibyte && character >= YP_MEMCHR_TRAILING_BYTE_MINIMUM) { + const char *source = (const char *) memory; + size_t index = 0; + + while (index < number) { + if (source[index] == character) { + return (void *) (source + index); + } + + size_t width = parser->encoding.char_width(source + index); + if (width == 0) { + return NULL; + } + + index += width; + } + + return NULL; + } else { + return memchr(memory, character, number); + } +} diff --git a/src/main/c/yarp/src/util/yp_newline_list.c b/src/main/c/yarp/src/util/yp_newline_list.c new file mode 100644 index 000000000000..c619e83c92e4 --- /dev/null +++ b/src/main/c/yarp/src/util/yp_newline_list.c @@ -0,0 +1,117 @@ +#include "yarp/util/yp_newline_list.h" + +// Initialize a new newline list with the given capacity. Returns true if the +// allocation of the offsets succeeds, otherwise returns false. +bool +yp_newline_list_init(yp_newline_list_t *list, const char *start, size_t capacity) { + list->offsets = (size_t *) calloc(capacity, sizeof(size_t)); + if (list->offsets == NULL) return false; + + list->start = start; + + // This is 1 instead of 0 because we want to include the first line of the + // file as having offset 0, which is set because of calloc. + list->size = 1; + list->capacity = capacity; + + list->last_index = 0; + list->last_offset = 0; + + return true; +} + +// Append a new offset to the newline list. Returns true if the reallocation of +// the offsets succeeds (if one was necessary), otherwise returns false. +bool +yp_newline_list_append(yp_newline_list_t *list, const char *cursor) { + if (list->size == list->capacity) { + list->capacity = list->capacity * 3 / 2; + list->offsets = (size_t *) realloc(list->offsets, list->capacity * sizeof(size_t)); + if (list->offsets == NULL) return false; + } + + assert(cursor >= list->start); + list->offsets[list->size++] = (size_t) (cursor - list->start + 1); + + return true; +} + +// Returns the line and column of the given offset, assuming we don't have any +// information about the previous index that we found. +static yp_line_column_t +yp_newline_list_line_column_search(yp_newline_list_t *list, size_t offset) { + size_t left = 0; + size_t right = list->size - 1; + + while (left <= right) { + size_t mid = left + (right - left) / 2; + + if (list->offsets[mid] == offset) { + return ((yp_line_column_t) { mid, 0 }); + } + + if (list->offsets[mid] < offset) { + left = mid + 1; + } else { + right = mid - 1; + } + } + + return ((yp_line_column_t) { left - 1, offset - list->offsets[left - 1] }); +} + +// Returns the line and column of the given offset, assuming we know the last +// index that we found. +static yp_line_column_t +yp_newline_list_line_column_scan(yp_newline_list_t *list, size_t offset) { + if (offset > list->last_offset) { + size_t index = list->last_index; + while (index < list->size && list->offsets[index] < offset) { + index++; + } + + if (index == list->size) { + return ((yp_line_column_t) { index - 1, offset - list->offsets[index - 1] }); + } + + return ((yp_line_column_t) { index, 0 }); + } else { + size_t index = list->last_index; + while (index > 0 && list->offsets[index] > offset) { + index--; + } + + if (index == 0) { + return ((yp_line_column_t) { 0, offset }); + } + + return ((yp_line_column_t) { index, offset - list->offsets[index - 1] }); + } +} + +// Returns the line and column of the given offset. If the offset is not in the +// list, the line and column of the closest offset less than the given offset +// are returned. +yp_line_column_t +yp_newline_list_line_column(yp_newline_list_t *list, const char *cursor) { + assert(cursor >= list->start); + size_t offset = (size_t) (cursor - list->start); + yp_line_column_t result; + + if (list->last_offset == 0) { + result = yp_newline_list_line_column_search(list, offset); + } else { + result = yp_newline_list_line_column_scan(list, offset); + } + + list->last_index = result.line; + list->last_offset = offset; + + return result; +} + +// Free the internal memory allocated for the newline list. +void +yp_newline_list_free(yp_newline_list_t *list) { + free(list->offsets); +} diff --git a/src/main/c/yarp/src/util/yp_snprintf.c b/src/main/c/yarp/src/util/yp_snprintf.c new file mode 100644 index 000000000000..2a28aacc6d5c --- /dev/null +++ b/src/main/c/yarp/src/util/yp_snprintf.c @@ -0,0 +1,12 @@ +#include "yarp/defines.h" + +// In case snprintf isn't present on the system, we provide our own that simply +// forwards to the less-safe sprintf. +int +yp_snprintf(char *dest, YP_ATTRIBUTE_UNUSED size_t size, const char *format, ...) { + va_list args; + va_start(args, format); + int result = vsprintf(dest, format, args); + va_end(args); + return result; +} diff --git a/src/main/c/yarp/src/yarp/src/util/yp_state_stack.c b/src/main/c/yarp/src/util/yp_state_stack.c similarity index 81% rename from src/main/c/yarp/src/yarp/src/util/yp_state_stack.c rename to src/main/c/yarp/src/util/yp_state_stack.c index efb0abf9f3ed..76c2eafceada 100644 --- a/src/main/c/yarp/src/yarp/src/util/yp_state_stack.c +++ b/src/main/c/yarp/src/util/yp_state_stack.c @@ -3,23 +3,23 @@ // Initializes the state stack to an empty stack. void yp_state_stack_init(yp_state_stack_t *stack) { - *stack = 0; + *stack = 0; } // Pushes a value onto the stack. void yp_state_stack_push(yp_state_stack_t *stack, bool value) { - *stack = (*stack << 1) | (value & 1); + *stack = (*stack << 1) | (value & 1); } // Pops a value off the stack. void yp_state_stack_pop(yp_state_stack_t *stack) { - *stack >>= 1; + *stack >>= 1; } // Returns the value at the top of the stack. bool yp_state_stack_p(yp_state_stack_t *stack) { - return *stack & 1; + return *stack & 1; } diff --git a/src/main/c/yarp/src/util/yp_string.c b/src/main/c/yarp/src/util/yp_string.c new file mode 100644 index 000000000000..248c082a8639 --- /dev/null +++ b/src/main/c/yarp/src/util/yp_string.c @@ -0,0 +1,88 @@ +#include "yarp/util/yp_string.h" + +// Initialize a shared string that is based on initial input. +void +yp_string_shared_init(yp_string_t *string, const char *start, const char *end) { + *string = (yp_string_t) { + .type = YP_STRING_SHARED, + .as.shared = { + .start = start, + .end = end + } + }; +} + +// Initialize an owned string that is responsible for freeing allocated memory. +void +yp_string_owned_init(yp_string_t *string, char *source, size_t length) { + *string = (yp_string_t) { + .type = YP_STRING_OWNED, + .as.owned = { + .source = source, + .length = length + } + }; +} + +// Initialize a constant string that doesn't own its memory source. +void +yp_string_constant_init(yp_string_t *string, const char *source, size_t length) { + *string = (yp_string_t) { + .type = YP_STRING_CONSTANT, + .as.constant = { + .source = source, + .length = length + } + }; +} + +// Returns the memory size associated with the string. +size_t +yp_string_memsize(const yp_string_t *string) { + size_t size = sizeof(yp_string_t); + if (string->type == YP_STRING_OWNED) { + size += string->as.owned.length; + } + return size; +} + +// Ensure the string is owned. If it is not, then reinitialize it as owned and +// copy over the previous source. +void +yp_string_ensure_owned(yp_string_t *string) { + if (string->type == YP_STRING_OWNED) return; + + size_t length = yp_string_length(string); + const char *source = yp_string_source(string); + + yp_string_owned_init(string, malloc(length), length); + memcpy(string->as.owned.source, source, length); +} + +// Returns the length associated with the string. +YP_EXPORTED_FUNCTION size_t +yp_string_length(const yp_string_t *string) { + if (string->type == YP_STRING_SHARED) { + return (size_t) (string->as.shared.end - string->as.shared.start); + } else { + return string->as.owned.length; + } +} + +// Returns the start pointer associated with the string. +YP_EXPORTED_FUNCTION const char * +yp_string_source(const yp_string_t *string) { + if (string->type == YP_STRING_SHARED) { + return string->as.shared.start; + } else { + return string->as.owned.source; + } +} + +// Free the associated memory of the given string. +YP_EXPORTED_FUNCTION void +yp_string_free(yp_string_t *string) { + if (string->type == YP_STRING_OWNED) { + free(string->as.owned.source); + } +} diff --git a/src/main/c/yarp/src/util/yp_string_list.c b/src/main/c/yarp/src/util/yp_string_list.c new file mode 100644 index 000000000000..74822729ff2e --- /dev/null +++ b/src/main/c/yarp/src/util/yp_string_list.c @@ -0,0 +1,32 @@ +#include "yarp/util/yp_string_list.h" + +// Allocate a new yp_string_list_t. +yp_string_list_t * +yp_string_list_alloc(void) { + return (yp_string_list_t *) malloc(sizeof(yp_string_list_t)); +} + +// Initialize a yp_string_list_t with its default values. +void +yp_string_list_init(yp_string_list_t *string_list) { + string_list->strings = (yp_string_t *) malloc(sizeof(yp_string_t)); + string_list->length = 0; + string_list->capacity = 1; +} + +// Append a yp_string_t to the given string list. +void +yp_string_list_append(yp_string_list_t *string_list, yp_string_t *string) { + if (string_list->length + 1 > string_list->capacity) { + string_list->capacity *= 2; + string_list->strings = (yp_string_t *) realloc(string_list->strings, string_list->capacity * sizeof(yp_string_t)); + } + + string_list->strings[string_list->length++] = *string; +} + +// Free the memory associated with the string list. +void +yp_string_list_free(yp_string_list_t *string_list) { + free(string_list->strings); +} diff --git a/src/main/c/yarp/src/util/yp_strncasecmp.c b/src/main/c/yarp/src/util/yp_strncasecmp.c new file mode 100644 index 000000000000..3c786f1c1f19 --- /dev/null +++ b/src/main/c/yarp/src/util/yp_strncasecmp.c @@ -0,0 +1,19 @@ +#include "yarp/defines.h" + +int +yp_strncasecmp(const char *string1, const char *string2, size_t length) { + size_t offset = 0; + int difference = 0; + + while (offset < length && string1[offset] != '\0') { + if (string2[offset] == '\0') return string1[offset]; + + unsigned char left = (unsigned char) string1[offset]; + unsigned char right = (unsigned char) string2[offset]; + + if ((difference = tolower(left) - tolower(right)) != 0) return difference; + offset++; + } + + return difference; +} diff --git a/src/main/c/yarp/src/util/yp_strpbrk.c b/src/main/c/yarp/src/util/yp_strpbrk.c new file mode 100644 index 000000000000..1c32398b559c --- /dev/null +++ b/src/main/c/yarp/src/util/yp_strpbrk.c @@ -0,0 +1,66 @@ +#include "yarp/util/yp_strpbrk.h" + +// This is the slow path that does care about the encoding. +static inline const char * +yp_strpbrk_multi_byte(yp_parser_t *parser, const char *source, const char *charset, size_t maximum) { + size_t index = 0; + + while (index < maximum) { + if (strchr(charset, source[index]) != NULL) { + return source + index; + } + + size_t width = parser->encoding.char_width(source + index); + if (width == 0) { + return NULL; + } + + index += width; + } + + return NULL; +} + +// This is the fast path that does not care about the encoding. +static inline const char * +yp_strpbrk_single_byte(const char *source, const char *charset, size_t maximum) { + size_t index = 0; + + while (index < maximum) { + if (strchr(charset, source[index]) != NULL) { + return source + index; + } + + index++; + } + + return NULL; +} + +// Here we have rolled our own version of strpbrk. The standard library strpbrk +// has undefined behavior when the source string is not null-terminated. We want +// to support strings that are not null-terminated because yp_parse does not +// have the contract that the string is null-terminated. (This is desirable +// because it means the extension can call yp_parse with the result of a call to +// mmap). +// +// The standard library strpbrk also does not support passing a maximum length +// to search. We want to support this for the reason mentioned above, but we +// also don't want it to stop on null bytes. Ruby actually allows null bytes +// within strings, comments, regular expressions, etc. So we need to be able to +// skip past them. +// +// Finally, we want to support encodings wherein the charset could contain +// characters that are trailing bytes of multi-byte characters. For example, in +// Shift-JIS, the backslash character can be a trailing byte. In that case we +// need to take a slower path and iterate one multi-byte character at a time. +const char * +yp_strpbrk(yp_parser_t *parser, const char *source, const char *charset, ptrdiff_t length) { + if (length <= 0) { + return NULL; + } else if (parser->encoding_changed && parser->encoding.multibyte) { + return yp_strpbrk_multi_byte(parser, source, charset, (size_t) length); + } else { + return yp_strpbrk_single_byte(source, charset, (size_t) length); + } +} diff --git a/src/main/c/yarp/src/yarp.c b/src/main/c/yarp/src/yarp.c new file mode 100644 index 000000000000..c87b37d5fb03 --- /dev/null +++ b/src/main/c/yarp/src/yarp.c @@ -0,0 +1,12896 @@ +#include "yarp.h" + +// The YP_VERSION macro is defined by the build system. If it is not +// present then we need to fail the build, since we're explicitly returning it +// from the yp_version function. +#ifndef YP_VERSION +#error "YP_VERSION must be defined" +#endif + +// The YARP version and the serialization format. +const char * +yp_version(void) { + return YP_VERSION; +} + +// In heredocs, tabs automatically complete up to the next 8 spaces. This is +// defined in CRuby as TAB_WIDTH. +#define YP_TAB_WHITESPACE_SIZE 8 + +// Debugging logging will provide you will additional debugging functions as +// well as automatically replace some functions with their debugging +// counterparts. +#ifndef YP_DEBUG_LOGGING +#define YP_DEBUG_LOGGING 0 +#endif + +#if YP_DEBUG_LOGGING + +/******************************************************************************/ +/* Debugging */ +/******************************************************************************/ + +YP_ATTRIBUTE_UNUSED static const char * +debug_context(yp_context_t context) { + switch (context) { + case YP_CONTEXT_BEGIN: return "BEGIN"; + case YP_CONTEXT_CLASS: return "CLASS"; + case YP_CONTEXT_CASE_IN: return "CASE_IN"; + case YP_CONTEXT_CASE_WHEN: return "CASE_WHEN"; + case YP_CONTEXT_DEF: return "DEF"; + case YP_CONTEXT_DEF_PARAMS: return "DEF_PARAMS"; + case YP_CONTEXT_DEFAULT_PARAMS: return "DEFAULT_PARAMS"; + case YP_CONTEXT_ENSURE: return "ENSURE"; + case YP_CONTEXT_ELSE: return "ELSE"; + case YP_CONTEXT_ELSIF: return "ELSIF"; + case YP_CONTEXT_EMBEXPR: return "EMBEXPR"; + case YP_CONTEXT_BLOCK_BRACES: return "BLOCK_BRACES"; + case YP_CONTEXT_BLOCK_KEYWORDS: return "BLOCK_KEYWORDS"; + case YP_CONTEXT_FOR: return "FOR"; + case YP_CONTEXT_IF: return "IF"; + case YP_CONTEXT_MAIN: return "MAIN"; + case YP_CONTEXT_MODULE: return "MODULE"; + case YP_CONTEXT_PARENS: return "PARENS"; + case YP_CONTEXT_POSTEXE: return "POSTEXE"; + case YP_CONTEXT_PREDICATE: return "PREDICATE"; + case YP_CONTEXT_PREEXE: return "PREEXE"; + case YP_CONTEXT_RESCUE: return "RESCUE"; + case YP_CONTEXT_RESCUE_ELSE: return "RESCUE_ELSE"; + case YP_CONTEXT_SCLASS: return "SCLASS"; + case YP_CONTEXT_UNLESS: return "UNLESS"; + case YP_CONTEXT_UNTIL: return "UNTIL"; + case YP_CONTEXT_WHILE: return "WHILE"; + case YP_CONTEXT_LAMBDA_BRACES: return "LAMBDA_BRACES"; + case YP_CONTEXT_LAMBDA_DO_END: return "LAMBDA_DO_END"; + } + return NULL; +} + +YP_ATTRIBUTE_UNUSED static void +debug_contexts(yp_parser_t *parser) { + yp_context_node_t *context_node = parser->current_context; + fprintf(stderr, "CONTEXTS: "); + + if (context_node != NULL) { + while (context_node != NULL) { + fprintf(stderr, "%s", debug_context(context_node->context)); + context_node = context_node->prev; + if (context_node != NULL) { + fprintf(stderr, " <- "); + } + } + } else { + fprintf(stderr, "NONE"); + } + + fprintf(stderr, "\n"); +} + +YP_ATTRIBUTE_UNUSED static void +debug_node(const char *message, yp_parser_t *parser, yp_node_t *node) { + yp_buffer_t buffer; + if (!yp_buffer_init(&buffer)) return; + + yp_prettyprint(parser, node, &buffer); + + fprintf(stderr, "%s\n%.*s\n", message, (int) buffer.length, buffer.value); + yp_buffer_free(&buffer); +} + +YP_ATTRIBUTE_UNUSED static void +debug_lex_mode(yp_parser_t *parser) { + yp_lex_mode_t *lex_mode = parser->lex_modes.current; + bool first = true; + + while (lex_mode != NULL) { + if (first) { + first = false; + } else { + fprintf(stderr, " <- "); + } + + switch (lex_mode->mode) { + case YP_LEX_DEFAULT: fprintf(stderr, "DEFAULT"); break; + case YP_LEX_EMBEXPR: fprintf(stderr, "EMBEXPR"); break; + case YP_LEX_EMBVAR: fprintf(stderr, "EMBVAR"); break; + case YP_LEX_HEREDOC: fprintf(stderr, "HEREDOC"); break; + case YP_LEX_LIST: fprintf(stderr, "LIST (terminator=%c, interpolation=%d)", lex_mode->as.list.terminator, lex_mode->as.list.interpolation); break; + case YP_LEX_REGEXP: fprintf(stderr, "REGEXP (terminator=%c)", lex_mode->as.regexp.terminator); break; + case YP_LEX_STRING: fprintf(stderr, "STRING (terminator=%c, interpolation=%d)", lex_mode->as.string.terminator, lex_mode->as.string.interpolation); break; + case YP_LEX_NUMERIC: fprintf(stderr, "NUMERIC (token_type=%s)", yp_token_type_to_str(lex_mode->as.numeric.type)); break; + } + + lex_mode = lex_mode->prev; + } + + fprintf(stderr, "\n"); +} + +YP_ATTRIBUTE_UNUSED static void +debug_state(yp_parser_t *parser) { + fprintf(stderr, "STATE: "); + bool first = true; + + if (parser->lex_state == YP_LEX_STATE_NONE) { + fprintf(stderr, "NONE\n"); + return; + } + +#define CHECK_STATE(state) \ + if (parser->lex_state & state) { \ + if (!first) fprintf(stderr, "|"); \ + fprintf(stderr, "%s", #state); \ + first = false; \ + } + + CHECK_STATE(YP_LEX_STATE_BEG) + CHECK_STATE(YP_LEX_STATE_END) + CHECK_STATE(YP_LEX_STATE_ENDARG) + CHECK_STATE(YP_LEX_STATE_ENDFN) + CHECK_STATE(YP_LEX_STATE_ARG) + CHECK_STATE(YP_LEX_STATE_CMDARG) + CHECK_STATE(YP_LEX_STATE_MID) + CHECK_STATE(YP_LEX_STATE_FNAME) + CHECK_STATE(YP_LEX_STATE_DOT) + CHECK_STATE(YP_LEX_STATE_CLASS) + CHECK_STATE(YP_LEX_STATE_LABEL) + CHECK_STATE(YP_LEX_STATE_LABELED) + CHECK_STATE(YP_LEX_STATE_FITEM) + +#undef CHECK_STATE + + fprintf(stderr, "\n"); +} + +YP_ATTRIBUTE_UNUSED static void +debug_token(yp_token_t * token) { + fprintf(stderr, "%s: \"%.*s\"\n", yp_token_type_to_str(token->type), (int) (token->end - token->start), token->start); +} + +#endif + +/******************************************************************************/ +/* Lex mode manipulations */ +/******************************************************************************/ + +// Returns the incrementor character that should be used to increment the +// nesting count if one is possible. +static inline char +lex_mode_incrementor(const char start) { + switch (start) { + case '(': + case '[': + case '{': + case '<': + return start; + default: + return '\0'; + } +} + +// Returns the matching character that should be used to terminate a list +// beginning with the given character. +static inline char +lex_mode_terminator(const char start) { + switch (start) { + case '(': + return ')'; + case '[': + return ']'; + case '{': + return '}'; + case '<': + return '>'; + default: + return start; + } +} + +// Push a new lex state onto the stack. If we're still within the pre-allocated +// space of the lex state stack, then we'll just use a new slot. Otherwise we'll +// allocate a new pointer and use that. +static bool +lex_mode_push(yp_parser_t *parser, yp_lex_mode_t lex_mode) { + lex_mode.prev = parser->lex_modes.current; + parser->lex_modes.index++; + + if (parser->lex_modes.index > YP_LEX_STACK_SIZE - 1) { + parser->lex_modes.current = (yp_lex_mode_t *) malloc(sizeof(yp_lex_mode_t)); + if (parser->lex_modes.current == NULL) return false; + + *parser->lex_modes.current = lex_mode; + } else { + parser->lex_modes.stack[parser->lex_modes.index] = lex_mode; + parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index]; + } + + return true; +} + +// Push on a new list lex mode. +static inline bool +lex_mode_push_list(yp_parser_t *parser, bool interpolation, char delimiter) { + char incrementor = lex_mode_incrementor(delimiter); + char terminator = lex_mode_terminator(delimiter); + + yp_lex_mode_t lex_mode = { + .mode = YP_LEX_LIST, + .as.list = { + .nesting = 0, + .interpolation = interpolation, + .incrementor = incrementor, + .terminator = terminator + } + }; + + // These are the places where we need to split up the content of the list. + // We'll use strpbrk to find the first of these characters. + char *breakpoints = lex_mode.as.list.breakpoints; + memcpy(breakpoints, "\\ \t\f\r\v\n\0\0\0", sizeof(lex_mode.as.list.breakpoints)); + + // Now we'll add the terminator to the list of breakpoints. + size_t index = 7; + breakpoints[index++] = terminator; + + // If interpolation is allowed, then we're going to check for the # + // character. Otherwise we'll only look for escapes and the terminator. + if (interpolation) { + breakpoints[index++] = '#'; + } + + // If there is an incrementor, then we'll check for that as well. + if (incrementor != '\0') { + breakpoints[index++] = incrementor; + } + + return lex_mode_push(parser, lex_mode); +} + +// Push on a new regexp lex mode. +static inline bool +lex_mode_push_regexp(yp_parser_t *parser, char incrementor, char terminator) { + yp_lex_mode_t lex_mode = { + .mode = YP_LEX_REGEXP, + .as.regexp = { + .nesting = 0, + .incrementor = incrementor, + .terminator = terminator + } + }; + + // These are the places where we need to split up the content of the + // regular expression. We'll use strpbrk to find the first of these + // characters. + char *breakpoints = lex_mode.as.regexp.breakpoints; + memcpy(breakpoints, "\n\\#\0\0", sizeof(lex_mode.as.regexp.breakpoints)); + + // First we'll add the terminator. + breakpoints[3] = terminator; + + // Next, if there is an incrementor, then we'll check for that as well. + if (incrementor != '\0') { + breakpoints[4] = incrementor; + } + + return lex_mode_push(parser, lex_mode); +} + +// Push on a new string lex mode. +static inline bool +lex_mode_push_string(yp_parser_t *parser, bool interpolation, bool label_allowed, char incrementor, char terminator) { + yp_lex_mode_t lex_mode = { + .mode = YP_LEX_STRING, + .as.string = { + .nesting = 0, + .interpolation = interpolation, + .label_allowed = label_allowed, + .incrementor = incrementor, + .terminator = terminator + } + }; + + // These are the places where we need to split up the content of the + // string. We'll use strpbrk to find the first of these characters. + char *breakpoints = lex_mode.as.string.breakpoints; + memcpy(breakpoints, "\n\\\0\0\0", sizeof(lex_mode.as.string.breakpoints)); + + // Now add in the terminator. + size_t index = 2; + breakpoints[index++] = terminator; + + // If interpolation is allowed, then we're going to check for the # + // character. Otherwise we'll only look for escapes and the terminator. + if (interpolation) { + breakpoints[index++] = '#'; + } + + // If we have an incrementor, then we'll add that in as a breakpoint as + // well. + if (incrementor != '\0') { + breakpoints[index++] = incrementor; + } + + return lex_mode_push(parser, lex_mode); +} + +// Pop the current lex state off the stack. If we're within the pre-allocated +// space of the lex state stack, then we'll just decrement the index. Otherwise +// we'll free the current pointer and use the previous pointer. +static void +lex_mode_pop(yp_parser_t *parser) { + if (parser->lex_modes.index == 0) { + parser->lex_modes.current->mode = YP_LEX_DEFAULT; + } else if (parser->lex_modes.index < YP_LEX_STACK_SIZE) { + parser->lex_modes.index--; + parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index]; + } else { + parser->lex_modes.index--; + yp_lex_mode_t *prev = parser->lex_modes.current->prev; + free(parser->lex_modes.current); + parser->lex_modes.current = prev; + } +} + +// This is the equivalent of IS_lex_state is CRuby. +static inline bool +lex_state_p(yp_parser_t *parser, yp_lex_state_t state) { + return parser->lex_state & state; +} + +typedef enum { + YP_IGNORED_NEWLINE_NONE = 0, + YP_IGNORED_NEWLINE_ALL, + YP_IGNORED_NEWLINE_PATTERN +} yp_ignored_newline_type_t; + +static inline yp_ignored_newline_type_t +lex_state_ignored_p(yp_parser_t *parser) { + bool ignored = lex_state_p(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_CLASS | YP_LEX_STATE_FNAME | YP_LEX_STATE_DOT) && !lex_state_p(parser, YP_LEX_STATE_LABELED); + + if (ignored) { + return YP_IGNORED_NEWLINE_ALL; + } else if (parser->lex_state == (YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED)) { + return YP_IGNORED_NEWLINE_PATTERN; + } else { + return YP_IGNORED_NEWLINE_NONE; + } +} + +static inline bool +lex_state_beg_p(yp_parser_t *parser) { + return lex_state_p(parser, YP_LEX_STATE_BEG_ANY) || (parser->lex_state == (YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED)); +} + +static inline bool +lex_state_arg_p(yp_parser_t *parser) { + return lex_state_p(parser, YP_LEX_STATE_ARG_ANY); +} + +static inline bool +lex_state_spcarg_p(yp_parser_t *parser, bool space_seen) { + return lex_state_arg_p(parser) && space_seen && !yp_char_is_whitespace(*parser->current.end); +} + +static inline bool +lex_state_end_p(yp_parser_t *parser) { + return lex_state_p(parser, YP_LEX_STATE_END_ANY); +} + +// This is the equivalent of IS_AFTER_OPERATOR in CRuby. +static inline bool +lex_state_operator_p(yp_parser_t *parser) { + return lex_state_p(parser, YP_LEX_STATE_FNAME | YP_LEX_STATE_DOT); +} + +// Set the state of the lexer. This is defined as a function to be able to put a breakpoint in it. +static inline void +lex_state_set(yp_parser_t *parser, yp_lex_state_t state) { + parser->lex_state = state; +} + +#if YP_DEBUG_LOGGING +static inline void +debug_lex_state_set(yp_parser_t *parser, yp_lex_state_t state, char const * caller_name, int line_number) { + fprintf(stderr, "Caller: %s:%d\nPrevious: ", caller_name, line_number); + debug_state(parser); + lex_state_set(parser, state); + fprintf(stderr, "Now: "); + debug_state(parser); + fprintf(stderr, "\n"); +} + +#define lex_state_set(parser, state) debug_lex_state_set(parser, state, __func__, __LINE__) +#endif + +/******************************************************************************/ +/* Node-related functions */ +/******************************************************************************/ + +// Retrieve the constant pool id for the given location. +static inline yp_constant_id_t +yp_parser_constant_id_location(yp_parser_t *parser, const char *start, const char *end) { + return yp_constant_pool_insert(&parser->constant_pool, start, (size_t) (end - start)); +} + +// Retrieve the constant pool id for the given token. +static inline yp_constant_id_t +yp_parser_constant_id_token(yp_parser_t *parser, const yp_token_t *token) { + return yp_parser_constant_id_location(parser, token->start, token->end); +} + +// In a lot of places in the tree you can have tokens that are not provided but +// that do not cause an error. For example, in a method call without +// parentheses. In these cases we set the token to the "not provided" type. For +// example: +// +// yp_token_t token; +// not_provided(&token, parser->previous.end); +// +static inline yp_token_t +not_provided(yp_parser_t *parser) { + return (yp_token_t) { .type = YP_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start }; +} + +#define YP_EMPTY_STRING ((yp_string_t) { .type = YP_STRING_SHARED, .as.shared.start = NULL, .as.shared.end = NULL }) +#define YP_LOCATION_NULL_VALUE(parser) ((yp_location_t) { .start = parser->start, .end = parser->start }) +#define YP_LOCATION_TOKEN_VALUE(token) ((yp_location_t) { .start = (token)->start, .end = (token)->end }) +#define YP_LOCATION_NODE_VALUE(node) ((yp_location_t) { .start = (node)->location.start, .end = (node)->location.end }) +#define YP_LOCATION_NODE_BASE_VALUE(node) ((yp_location_t) { .start = (node)->base.location.start, .end = (node)->base.location.end }) +#define YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE ((yp_location_t) { .start = NULL, .end = NULL }) +#define YP_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == YP_TOKEN_NOT_PROVIDED ? YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE : YP_LOCATION_TOKEN_VALUE(token)) +#define YP_TOKEN_NOT_PROVIDED_VALUE(parser) ((yp_token_t) { .type = YP_TOKEN_NOT_PROVIDED, .start = (parser)->start, .end = (parser)->start }) + +// This is a special out parameter to the parse_arguments_list function that +// includes opening and closing parentheses in addition to the arguments since +// it's so common. It is handy to use when passing argument information to one +// of the call node creation functions. +typedef struct { + yp_location_t opening_loc; + yp_arguments_node_t *arguments; + yp_location_t closing_loc; + yp_block_node_t *block; +} yp_arguments_t; + +#define YP_EMPTY_ARGUMENTS ((yp_arguments_t) { .opening_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .arguments = NULL, .closing_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, .block = NULL }) + +/******************************************************************************/ +/* Node creation functions */ +/******************************************************************************/ + +// Parse out the options for a regular expression. +static inline uint32_t +yp_regular_expression_flags_create(const yp_token_t *closing) { + uint32_t flags = 0; + + if (closing->type == YP_TOKEN_REGEXP_END) { + for (const char *flag = closing->start + 1; flag < closing->end; flag++) { + switch (*flag) { + case 'i': flags |= YP_REGULAR_EXPRESSION_FLAGS_IGNORE_CASE; break; + case 'm': flags |= YP_REGULAR_EXPRESSION_FLAGS_MULTI_LINE; break; + case 'x': flags |= YP_REGULAR_EXPRESSION_FLAGS_EXTENDED; break; + case 'e': flags |= YP_REGULAR_EXPRESSION_FLAGS_EUC_JP; break; + case 'n': flags |= YP_REGULAR_EXPRESSION_FLAGS_ASCII_8BIT; break; + case 's': flags |= YP_REGULAR_EXPRESSION_FLAGS_WINDOWS_31J; break; + case 'u': flags |= YP_REGULAR_EXPRESSION_FLAGS_UTF_8; break; + case 'o': flags |= YP_REGULAR_EXPRESSION_FLAGS_ONCE; break; + default: assert(false && "unreachable"); + } + } + } + + return flags; +} + +// Allocate and initialize a new StatementsNode node. +static yp_statements_node_t * +yp_statements_node_create(yp_parser_t *parser); + +// Append a new node to the given StatementsNode node's body. +static void +yp_statements_node_body_append(yp_statements_node_t *node, yp_node_t *statement); + +// This function is here to allow us a place to extend in the future when we +// implement our own arena allocation. +static inline void * +yp_alloc_node(YP_ATTRIBUTE_UNUSED yp_parser_t *parser, size_t size) { + return malloc(size); +} + +#define YP_ALLOC_NODE(parser, type) (type *) yp_alloc_node(parser, sizeof(type)); if (node == NULL) return NULL + +// Allocate a new MissingNode node. +static yp_missing_node_t * +yp_missing_node_create(yp_parser_t *parser, const char *start, const char *end) { + yp_missing_node_t *node = YP_ALLOC_NODE(parser, yp_missing_node_t); + *node = (yp_missing_node_t) {{ .type = YP_NODE_MISSING_NODE, .location = { .start = start, .end = end } }}; + return node; +} + +// Allocate and initialize a new alias node. +static yp_alias_node_t * +yp_alias_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *new_name, yp_node_t *old_name) { + assert(keyword->type == YP_TOKEN_KEYWORD_ALIAS); + yp_alias_node_t *node = YP_ALLOC_NODE(parser, yp_alias_node_t); + + *node = (yp_alias_node_t) { + { + .type = YP_NODE_ALIAS_NODE, + .location = { + .start = keyword->start, + .end = old_name->location.end + }, + }, + .new_name = new_name, + .old_name = old_name, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword) + }; + + return node; +} + +// Allocate a new AlternationPatternNode node. +static yp_alternation_pattern_node_t * +yp_alternation_pattern_node_create(yp_parser_t *parser, yp_node_t *left, yp_node_t *right, const yp_token_t *operator) { + yp_alternation_pattern_node_t *node = YP_ALLOC_NODE(parser, yp_alternation_pattern_node_t); + + *node = (yp_alternation_pattern_node_t) { + { + .type = YP_NODE_ALTERNATION_PATTERN_NODE, + .location = { + .start = left->location.start, + .end = right->location.end + }, + }, + .left = left, + .right = right, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +// Allocate and initialize a new and node. +static yp_and_node_t * +yp_and_node_create(yp_parser_t *parser, yp_node_t *left, const yp_token_t *operator, yp_node_t *right) { + yp_and_node_t *node = YP_ALLOC_NODE(parser, yp_and_node_t); + + *node = (yp_and_node_t) { + { + .type = YP_NODE_AND_NODE, + .location = { + .start = left->location.start, + .end = right->location.end + }, + }, + .left = left, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .right = right + }; + + return node; +} + +// Allocate an initialize a new arguments node. +static yp_arguments_node_t * +yp_arguments_node_create(yp_parser_t *parser) { + yp_arguments_node_t *node = YP_ALLOC_NODE(parser, yp_arguments_node_t); + + *node = (yp_arguments_node_t) { + { + .type = YP_NODE_ARGUMENTS_NODE, + .location = YP_LOCATION_NULL_VALUE(parser) + }, + .arguments = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Return the size of the given arguments node. +static size_t +yp_arguments_node_size(yp_arguments_node_t *node) { + return node->arguments.size; +} + +// Append an argument to an arguments node. +static void +yp_arguments_node_arguments_append(yp_arguments_node_t *node, yp_node_t *argument) { + if (yp_arguments_node_size(node) == 0) { + node->base.location.start = argument->location.start; + } + + node->base.location.end = argument->location.end; + yp_node_list_append(&node->arguments, argument); +} + +// Allocate and initialize a new ArrayNode node. +static yp_array_node_t * +yp_array_node_create(yp_parser_t *parser, const yp_token_t *opening) { + yp_array_node_t *node = YP_ALLOC_NODE(parser, yp_array_node_t); + + *node = (yp_array_node_t) { + { + .type = YP_NODE_ARRAY_NODE, + .location = { + .start = opening->start, + .end = opening->end + }, + }, + .opening_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .elements = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Return the size of the given array node. +static inline size_t +yp_array_node_size(yp_array_node_t *node) { + return node->elements.size; +} + +// Append an argument to an array node. +static inline void +yp_array_node_elements_append(yp_array_node_t *node, yp_node_t *element) { + if (!node->elements.size && !node->opening_loc.start) { + node->base.location.start = element->location.start; + } + yp_node_list_append(&node->elements, element); + node->base.location.end = element->location.end; +} + +// Set the closing token and end location of an array node. +static void +yp_array_node_close_set(yp_array_node_t *node, const yp_token_t *closing) { + assert(closing->type == YP_TOKEN_BRACKET_RIGHT || closing->type == YP_TOKEN_STRING_END || closing->type == YP_TOKEN_MISSING || closing->type == YP_TOKEN_NOT_PROVIDED); + node->base.location.end = closing->end; + node->closing_loc = YP_LOCATION_TOKEN_VALUE(closing); +} + +// Allocate and initialize a new array pattern node. The node list given in the +// nodes parameter is guaranteed to have at least two nodes. +static yp_array_pattern_node_t * +yp_array_pattern_node_node_list_create(yp_parser_t *parser, yp_node_list_t *nodes) { + yp_array_pattern_node_t *node = YP_ALLOC_NODE(parser, yp_array_pattern_node_t); + + *node = (yp_array_pattern_node_t) { + { + .type = YP_NODE_ARRAY_PATTERN_NODE, + .location = { + .start = nodes->nodes[0]->location.start, + .end = nodes->nodes[nodes->size - 1]->location.end + }, + }, + .constant = NULL, + .rest = NULL, + .requireds = YP_EMPTY_NODE_LIST, + .posts = YP_EMPTY_NODE_LIST, + .opening_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + // For now we're going to just copy over each pointer manually. This could be + // much more efficient, as we could instead resize the node list. + bool found_rest = false; + for (size_t index = 0; index < nodes->size; index++) { + yp_node_t *child = nodes->nodes[index]; + + if (child->type == YP_NODE_SPLAT_NODE) { + node->rest = child; + found_rest = true; + } else if (found_rest) { + yp_node_list_append(&node->posts, child); + } else { + yp_node_list_append(&node->requireds, child); + } + } + + return node; +} + +// Allocate and initialize a new array pattern node from a single rest node. +static yp_array_pattern_node_t * +yp_array_pattern_node_rest_create(yp_parser_t *parser, yp_node_t *rest) { + yp_array_pattern_node_t *node = YP_ALLOC_NODE(parser, yp_array_pattern_node_t); + + *node = (yp_array_pattern_node_t) { + { + .type = YP_NODE_ARRAY_PATTERN_NODE, + .location = rest->location, + }, + .constant = NULL, + .rest = rest, + .requireds = YP_EMPTY_NODE_LIST, + .posts = YP_EMPTY_NODE_LIST, + .opening_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +// Allocate and initialize a new array pattern node from a constant and opening +// and closing tokens. +static yp_array_pattern_node_t * +yp_array_pattern_node_constant_create(yp_parser_t *parser, yp_node_t *constant, const yp_token_t *opening, const yp_token_t *closing) { + yp_array_pattern_node_t *node = YP_ALLOC_NODE(parser, yp_array_pattern_node_t); + + *node = (yp_array_pattern_node_t) { + { + .type = YP_NODE_ARRAY_PATTERN_NODE, + .location = { + .start = constant->location.start, + .end = closing->end + }, + }, + .constant = constant, + .rest = NULL, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_LOCATION_TOKEN_VALUE(closing), + .requireds = YP_EMPTY_NODE_LIST, + .posts = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Allocate and initialize a new array pattern node from an opening and closing +// token. +static yp_array_pattern_node_t * +yp_array_pattern_node_empty_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *closing) { + yp_array_pattern_node_t *node = YP_ALLOC_NODE(parser, yp_array_pattern_node_t); + + *node = (yp_array_pattern_node_t) { + { + .type = YP_NODE_ARRAY_PATTERN_NODE, + .location = { + .start = opening->start, + .end = closing->end + }, + }, + .constant = NULL, + .rest = NULL, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_LOCATION_TOKEN_VALUE(closing), + .requireds = YP_EMPTY_NODE_LIST, + .posts = YP_EMPTY_NODE_LIST + }; + + return node; +} + +static inline void +yp_array_pattern_node_requireds_append(yp_array_pattern_node_t *node, yp_node_t *inner) { + yp_node_list_append(&node->requireds, inner); +} + +// Allocate and initialize a new assoc node. +static yp_assoc_node_t * +yp_assoc_node_create(yp_parser_t *parser, yp_node_t *key, const yp_token_t *operator, yp_node_t *value) { + yp_assoc_node_t *node = YP_ALLOC_NODE(parser, yp_assoc_node_t); + const char *end; + + if (value != NULL) { + end = value->location.end; + } else if (operator->type != YP_TOKEN_NOT_PROVIDED) { + end = operator->end; + } else { + end = key->location.end; + } + + *node = (yp_assoc_node_t) { + { + .type = YP_NODE_ASSOC_NODE, + .location = { + .start = key->location.start, + .end = end + }, + }, + .key = key, + .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new assoc splat node. +static yp_assoc_splat_node_t * +yp_assoc_splat_node_create(yp_parser_t *parser, yp_node_t *value, const yp_token_t *operator) { + assert(operator->type == YP_TOKEN_USTAR_STAR); + yp_assoc_splat_node_t *node = YP_ALLOC_NODE(parser, yp_assoc_splat_node_t); + + *node = (yp_assoc_splat_node_t) { + { + .type = YP_NODE_ASSOC_SPLAT_NODE, + .location = { + .start = operator->start, + .end = value == NULL ? operator->end : value->location.end + }, + }, + .value = value, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +// Allocate a new BackReferenceReadNode node. +static yp_back_reference_read_node_t * +yp_back_reference_read_node_create(yp_parser_t *parser, const yp_token_t *name) { + assert(name->type == YP_TOKEN_BACK_REFERENCE); + yp_back_reference_read_node_t *node = YP_ALLOC_NODE(parser, yp_back_reference_read_node_t); + + *node = (yp_back_reference_read_node_t) { + { + .type = YP_NODE_BACK_REFERENCE_READ_NODE, + .location = YP_LOCATION_TOKEN_VALUE(name), + } + }; + + return node; +} + +// Allocate and initialize new a begin node. +static yp_begin_node_t * +yp_begin_node_create(yp_parser_t *parser, const yp_token_t *begin_keyword, yp_statements_node_t *statements) { + yp_begin_node_t *node = YP_ALLOC_NODE(parser, yp_begin_node_t); + + *node = (yp_begin_node_t) { + { + .type = YP_NODE_BEGIN_NODE, + .location = { + .start = begin_keyword->start, + .end = statements == NULL ? begin_keyword->end : statements->base.location.end + }, + }, + .begin_keyword_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(begin_keyword), + .statements = statements, + .end_keyword_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +// Set the rescue clause, optionally start, and end location of a begin node. +static void +yp_begin_node_rescue_clause_set(yp_begin_node_t *node, yp_rescue_node_t *rescue_clause) { + // If the begin keyword doesn't exist, we set the start on the begin_node + if (!node->begin_keyword_loc.start) { + node->base.location.start = rescue_clause->base.location.start; + } + node->base.location.end = rescue_clause->base.location.end; + node->rescue_clause = rescue_clause; +} + +// Set the else clause and end location of a begin node. +static void +yp_begin_node_else_clause_set(yp_begin_node_t *node, yp_else_node_t *else_clause) { + node->base.location.end = else_clause->base.location.end; + node->else_clause = else_clause; +} + +// Set the ensure clause and end location of a begin node. +static void +yp_begin_node_ensure_clause_set(yp_begin_node_t *node, yp_ensure_node_t *ensure_clause) { + node->base.location.end = ensure_clause->base.location.end; + node->ensure_clause = ensure_clause; +} + +// Set the end keyword and end location of a begin node. +static void +yp_begin_node_end_keyword_set(yp_begin_node_t *node, const yp_token_t *end_keyword) { + assert(end_keyword->type == YP_TOKEN_KEYWORD_END || end_keyword->type == YP_TOKEN_MISSING); + + node->base.location.end = end_keyword->end; + node->end_keyword_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword); +} + +// Allocate and initialize a new BlockArgumentNode node. +static yp_block_argument_node_t * +yp_block_argument_node_create(yp_parser_t *parser, const yp_token_t *operator, yp_node_t *expression) { + yp_block_argument_node_t *node = YP_ALLOC_NODE(parser, yp_block_argument_node_t); + + *node = (yp_block_argument_node_t) { + { + .type = YP_NODE_BLOCK_ARGUMENT_NODE, + .location = { + .start = operator->start, + .end = expression == NULL ? operator->end : expression->location.end + }, + }, + .expression = expression, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +// Allocate and initialize a new BlockNode node. +static yp_block_node_t * +yp_block_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const yp_token_t *opening, yp_block_parameters_node_t *parameters, yp_node_t *statements, const yp_token_t *closing) { + yp_block_node_t *node = YP_ALLOC_NODE(parser, yp_block_node_t); + + *node = (yp_block_node_t) { + { + .type = YP_NODE_BLOCK_NODE, + .location = { .start = opening->start, .end = closing->end }, + }, + .locals = *locals, + .parameters = parameters, + .statements = statements, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) + }; + + return node; +} + +// Allocate and initialize a new BlockParameterNode node. +static yp_block_parameter_node_t * +yp_block_parameter_node_create(yp_parser_t *parser, const yp_token_t *name, const yp_token_t *operator) { + assert(operator->type == YP_TOKEN_NOT_PROVIDED || operator->type == YP_TOKEN_AMPERSAND); + yp_block_parameter_node_t *node = YP_ALLOC_NODE(parser, yp_block_parameter_node_t); + + *node = (yp_block_parameter_node_t) { + { + .type = YP_NODE_BLOCK_PARAMETER_NODE, + .location = { + .start = operator->start, + .end = (name->type == YP_TOKEN_NOT_PROVIDED ? operator->end : name->end) + }, + }, + .name_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(name), + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +// Allocate and initialize a new BlockParametersNode node. +static yp_block_parameters_node_t * +yp_block_parameters_node_create(yp_parser_t *parser, yp_parameters_node_t *parameters, const yp_token_t *opening) { + yp_block_parameters_node_t *node = YP_ALLOC_NODE(parser, yp_block_parameters_node_t); + + const char *start; + if (opening->type != YP_TOKEN_NOT_PROVIDED) { + start = opening->start; + } else if (parameters != NULL) { + start = parameters->base.location.start; + } else { + start = NULL; + } + + const char *end; + if (parameters != NULL) { + end = parameters->base.location.end; + } else if (opening->type != YP_TOKEN_NOT_PROVIDED) { + end = opening->end; + } else { + end = NULL; + } + + *node = (yp_block_parameters_node_t) { + { + .type = YP_NODE_BLOCK_PARAMETERS_NODE, + .location = { + .start = start, + .end = end + } + }, + .parameters = parameters, + .opening_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = { .start = NULL, .end = NULL }, + .locals = YP_EMPTY_LOCATION_LIST + }; + + return node; +} + +// Set the closing location of a BlockParametersNode node. +static void +yp_block_parameters_node_closing_set(yp_block_parameters_node_t *node, const yp_token_t *closing) { + assert(closing->type == YP_TOKEN_PIPE || closing->type == YP_TOKEN_PARENTHESIS_RIGHT || closing->type == YP_TOKEN_MISSING); + + node->base.location.end = closing->end; + node->closing_loc = YP_LOCATION_TOKEN_VALUE(closing); +} + +// Append a new block-local variable to a BlockParametersNode node. +static void +yp_block_parameters_node_append_local(yp_block_parameters_node_t *node, const yp_token_t *local) { + assert(local->type == YP_TOKEN_IDENTIFIER); + + yp_location_list_append(&node->locals, local); + if (node->base.location.start == NULL) node->base.location.start = local->start; + node->base.location.end = local->end; +} + +// Allocate and initialize a new BreakNode node. +static yp_break_node_t * +yp_break_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_arguments_node_t *arguments) { + assert(keyword->type == YP_TOKEN_KEYWORD_BREAK); + yp_break_node_t *node = YP_ALLOC_NODE(parser, yp_break_node_t); + + *node = (yp_break_node_t) { + { + .type = YP_NODE_BREAK_NODE, + .location = { + .start = keyword->start, + .end = (arguments == NULL ? keyword->end : arguments->base.location.end) + }, + }, + .arguments = arguments, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword) + }; + + return node; +} + +// Allocate and initialize a new CallNode node. This sets everything to NULL or +// YP_TOKEN_NOT_PROVIDED as appropriate such that its values can be overridden +// in the various specializations of this function. +static yp_call_node_t * +yp_call_node_create(yp_parser_t *parser) { + yp_call_node_t *node = YP_ALLOC_NODE(parser, yp_call_node_t); + + *node = (yp_call_node_t) { + { + .type = YP_NODE_CALL_NODE, + .location = YP_LOCATION_NULL_VALUE(parser), + }, + .receiver = NULL, + .operator_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .message_loc = YP_LOCATION_NULL_VALUE(parser), + .opening_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .arguments = NULL, + .closing_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .block = NULL, + .flags = 0 + }; + + return node; +} + +// Allocate and initialize a new CallNode node from an aref or an aset +// expression. +static yp_call_node_t * +yp_call_node_aref_create(yp_parser_t *parser, yp_node_t *receiver, yp_arguments_t *arguments) { + yp_call_node_t *node = yp_call_node_create(parser); + + node->base.location.start = receiver->location.start; + if (arguments->block != NULL) { + node->base.location.end = arguments->block->base.location.end; + } else { + node->base.location.end = arguments->closing_loc.end; + } + + node->receiver = receiver; + node->message_loc.start = arguments->opening_loc.start; + node->message_loc.end = arguments->closing_loc.end; + + node->opening_loc = arguments->opening_loc; + node->arguments = arguments->arguments; + node->closing_loc = arguments->closing_loc; + node->block = arguments->block; + + yp_string_constant_init(&node->name, "[]", 2); + return node; +} + +// Allocate and initialize a new CallNode node from a binary expression. +static yp_call_node_t * +yp_call_node_binary_create(yp_parser_t *parser, yp_node_t *receiver, yp_token_t *operator, yp_node_t *argument) { + yp_call_node_t *node = yp_call_node_create(parser); + + node->base.location.start = receiver->location.start; + node->base.location.end = argument->location.end; + + node->receiver = receiver; + node->message_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator); + + yp_arguments_node_t *arguments = yp_arguments_node_create(parser); + yp_arguments_node_arguments_append(arguments, argument); + node->arguments = arguments; + + yp_string_shared_init(&node->name, operator->start, operator->end); + return node; +} + +// Allocate and initialize a new CallNode node from a call expression. +static yp_call_node_t * +yp_call_node_call_create(yp_parser_t *parser, yp_node_t *receiver, yp_token_t *operator, yp_token_t *message, yp_arguments_t *arguments) { + yp_call_node_t *node = yp_call_node_create(parser); + + node->base.location.start = receiver->location.start; + if (arguments->block != NULL) { + node->base.location.end = arguments->block->base.location.end; + } else if (arguments->closing_loc.start != NULL) { + node->base.location.end = arguments->closing_loc.end; + } else if (arguments->arguments != NULL) { + node->base.location.end = arguments->arguments->base.location.end; + } else { + node->base.location.end = message->end; + } + + node->receiver = receiver; + node->operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator); + node->message_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(message); + node->opening_loc = arguments->opening_loc; + node->arguments = arguments->arguments; + node->closing_loc = arguments->closing_loc; + node->block = arguments->block; + + if (operator->type == YP_TOKEN_AMPERSAND_DOT) { + node->flags |= YP_CALL_NODE_FLAGS_SAFE_NAVIGATION; + } + + yp_string_shared_init(&node->name, message->start, message->end); + return node; +} + +// Allocate and initialize a new CallNode node from a call to a method name +// without a receiver that could not have been a local variable read. +static yp_call_node_t * +yp_call_node_fcall_create(yp_parser_t *parser, yp_token_t *message, yp_arguments_t *arguments) { + yp_call_node_t *node = yp_call_node_create(parser); + + node->base.location.start = message->start; + if (arguments->block != NULL) { + node->base.location.end = arguments->block->base.location.end; + } else if (arguments->closing_loc.start != NULL) { + node->base.location.end = arguments->closing_loc.end; + } else if (arguments->arguments != NULL) { + node->base.location.end = arguments->arguments->base.location.end; + } else { + node->base.location.end = arguments->closing_loc.end; + } + + node->message_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(message); + node->opening_loc = arguments->opening_loc; + node->arguments = arguments->arguments; + node->closing_loc = arguments->closing_loc; + node->block = arguments->block; + + yp_string_shared_init(&node->name, message->start, message->end); + return node; +} + +// Allocate and initialize a new CallNode node from a not expression. +static yp_call_node_t * +yp_call_node_not_create(yp_parser_t *parser, yp_node_t *receiver, yp_token_t *message, yp_arguments_t *arguments) { + yp_call_node_t *node = yp_call_node_create(parser); + + node->base.location.start = message->start; + if (arguments->closing_loc.start != NULL) { + node->base.location.end = arguments->closing_loc.end; + } else { + node->base.location.end = receiver->location.end; + } + + node->receiver = receiver; + node->message_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(message); + node->opening_loc = arguments->opening_loc; + node->arguments = arguments->arguments; + node->closing_loc = arguments->closing_loc; + + yp_string_constant_init(&node->name, "!", 1); + return node; +} + +// Allocate and initialize a new CallNode node from a call shorthand expression. +static yp_call_node_t * +yp_call_node_shorthand_create(yp_parser_t *parser, yp_node_t *receiver, yp_token_t *operator, yp_arguments_t *arguments) { + yp_call_node_t *node = yp_call_node_create(parser); + + node->base.location.start = receiver->location.start; + if (arguments->block != NULL) { + node->base.location.end = arguments->block->base.location.end; + } else { + node->base.location.end = arguments->closing_loc.end; + } + + node->receiver = receiver; + node->operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator); + node->opening_loc = arguments->opening_loc; + node->arguments = arguments->arguments; + node->closing_loc = arguments->closing_loc; + node->block = arguments->block; + + if (operator->type == YP_TOKEN_AMPERSAND_DOT) { + node->flags |= YP_CALL_NODE_FLAGS_SAFE_NAVIGATION; + } + + yp_string_constant_init(&node->name, "call", 4); + return node; +} + +// Allocate and initialize a new CallNode node from a unary operator expression. +static yp_call_node_t * +yp_call_node_unary_create(yp_parser_t *parser, yp_token_t *operator, yp_node_t *receiver, const char *name) { + yp_call_node_t *node = yp_call_node_create(parser); + + node->base.location.start = operator->start; + node->base.location.end = receiver->location.end; + + node->receiver = receiver; + node->message_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator); + + yp_string_constant_init(&node->name, name, strlen(name)); + return node; +} + +// Allocate and initialize a new CallNode node from a call to a method name +// without a receiver that could also have been a local variable read. +static yp_call_node_t * +yp_call_node_vcall_create(yp_parser_t *parser, yp_token_t *message) { + yp_call_node_t *node = yp_call_node_create(parser); + + node->base.location.start = message->start; + node->base.location.end = message->end; + + node->message_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(message); + + yp_string_shared_init(&node->name, message->start, message->end); + return node; +} + +// Returns whether or not this call node is a "vcall" (a call to a method name +// without a receiver that could also have been a local variable read). +static inline bool +yp_call_node_vcall_p(yp_call_node_t *node) { + return ( + (node->opening_loc.start == NULL) && + (node->arguments == NULL) && + (node->block == NULL) && + (node->receiver == NULL) + ); +} + +// Allocate and initialize a new CallOperatorAndWriteNode node. +static yp_call_operator_and_write_node_t * +yp_call_operator_and_write_node_create(yp_parser_t *parser, yp_call_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + yp_call_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_call_operator_and_write_node_t); + + *node = (yp_call_operator_and_write_node_t) { + { + .type = YP_NODE_CALL_OPERATOR_AND_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .target = target, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate a new CallOperatorWriteNode node. +static yp_call_operator_write_node_t * +yp_call_operator_write_node_create(yp_parser_t *parser, yp_call_node_t *target, const yp_token_t *operator, yp_node_t *value) { + yp_call_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_call_operator_write_node_t); + + *node = (yp_call_operator_write_node_t) { + { + .type = YP_NODE_CALL_OPERATOR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .target = target, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator_id = yp_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +// Allocate and initialize a new CallOperatorOrWriteNode node. +static yp_call_operator_or_write_node_t * +yp_call_operator_or_write_node_create(yp_parser_t *parser, yp_call_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL); + yp_call_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_call_operator_or_write_node_t); + + *node = (yp_call_operator_or_write_node_t) { + { + .type = YP_NODE_CALL_OPERATOR_OR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .target = target, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new CapturePatternNode node. +static yp_capture_pattern_node_t * +yp_capture_pattern_node_create(yp_parser_t *parser, yp_node_t *value, yp_node_t *target, const yp_token_t *operator) { + yp_capture_pattern_node_t *node = YP_ALLOC_NODE(parser, yp_capture_pattern_node_t); + + *node = (yp_capture_pattern_node_t) { + { + .type = YP_NODE_CAPTURE_PATTERN_NODE, + .location = { + .start = value->location.start, + .end = target->location.end + }, + }, + .value = value, + .target = target, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +// Allocate and initialize a new CaseNode node. +static yp_case_node_t * +yp_case_node_create(yp_parser_t *parser, const yp_token_t *case_keyword, yp_node_t *predicate, yp_else_node_t *consequent, const yp_token_t *end_keyword) { + yp_case_node_t *node = YP_ALLOC_NODE(parser, yp_case_node_t); + + *node = (yp_case_node_t) { + { + .type = YP_NODE_CASE_NODE, + .location = { + .start = case_keyword->start, + .end = end_keyword->end + }, + }, + .predicate = predicate, + .consequent = consequent, + .case_keyword_loc = YP_LOCATION_TOKEN_VALUE(case_keyword), + .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword), + .conditions = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Append a new condition to a CaseNode node. +static void +yp_case_node_condition_append(yp_case_node_t *node, yp_node_t *condition) { + assert(condition->type == YP_NODE_WHEN_NODE || condition->type == YP_NODE_IN_NODE); + + yp_node_list_append(&node->conditions, condition); + node->base.location.end = condition->location.end; +} + +// Set the consequent of a CaseNode node. +static void +yp_case_node_consequent_set(yp_case_node_t *node, yp_else_node_t *consequent) { + node->consequent = consequent; + node->base.location.end = consequent->base.location.end; +} + +// Set the end location for a CaseNode node. +static void +yp_case_node_end_keyword_loc_set(yp_case_node_t *node, const yp_token_t *end_keyword) { + node->base.location.end = end_keyword->end; + node->end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword); +} + +// Allocate a new ClassNode node. +static yp_class_node_t * +yp_class_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const yp_token_t *class_keyword, yp_node_t *constant_path, const yp_token_t *inheritance_operator, yp_node_t *superclass, yp_node_t *statements, const yp_token_t *end_keyword) { + yp_class_node_t *node = YP_ALLOC_NODE(parser, yp_class_node_t); + + *node = (yp_class_node_t) { + { + .type = YP_NODE_CLASS_NODE, + .location = { .start = class_keyword->start, .end = end_keyword->end }, + }, + .locals = *locals, + .class_keyword_loc = YP_LOCATION_TOKEN_VALUE(class_keyword), + .constant_path = constant_path, + .inheritance_operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(inheritance_operator), + .superclass = superclass, + .statements = statements, + .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +// Allocate and initialize a new ClassVariableOperatorAndWriteNode node. +static yp_class_variable_operator_and_write_node_t * +yp_class_variable_operator_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(target->type == YP_NODE_CLASS_VARIABLE_READ_NODE); + assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + yp_class_variable_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_operator_and_write_node_t); + + *node = (yp_class_variable_operator_and_write_node_t) { + { + .type = YP_NODE_CLASS_VARIABLE_OPERATOR_AND_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new ClassVariableOperatorWriteNode node. +static yp_class_variable_operator_write_node_t * +yp_class_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + yp_class_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_operator_write_node_t); + + *node = (yp_class_variable_operator_write_node_t) { + { + .type = YP_NODE_CLASS_VARIABLE_OPERATOR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +// Allocate and initialize a new ClassVariableOperatorOrWriteNode node. +static yp_class_variable_operator_or_write_node_t * +yp_class_variable_operator_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(target->type == YP_NODE_CLASS_VARIABLE_READ_NODE); + assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL); + yp_class_variable_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_operator_or_write_node_t); + + *node = (yp_class_variable_operator_or_write_node_t) { + { + .type = YP_NODE_CLASS_VARIABLE_OPERATOR_OR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new ClassVariableReadNode node. +static yp_class_variable_read_node_t * +yp_class_variable_read_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_CLASS_VARIABLE); + yp_class_variable_read_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_read_node_t); + *node = (yp_class_variable_read_node_t) {{ .type = YP_NODE_CLASS_VARIABLE_READ_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Initialize a new ClassVariableWriteNode node from a ClassVariableRead node. +static yp_class_variable_write_node_t * +yp_class_variable_read_node_to_class_variable_write_node(yp_parser_t *parser, yp_class_variable_read_node_t *read_node, yp_token_t *operator, yp_node_t *value) { + yp_class_variable_write_node_t *node = YP_ALLOC_NODE(parser, yp_class_variable_write_node_t); + + *node = (yp_class_variable_write_node_t) { + { + .type = YP_NODE_CLASS_VARIABLE_WRITE_NODE, + .location = { + .start = read_node->base.location.start, + .end = value != NULL ? value->location.end : read_node->base.location.end + }, + }, + .name_loc = YP_LOCATION_NODE_VALUE((yp_node_t *)read_node), + .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new ConstantPathOperatorAndWriteNode node. +static yp_constant_path_operator_and_write_node_t * +yp_constant_path_operator_and_write_node_create(yp_parser_t *parser, yp_constant_path_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + yp_constant_path_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_operator_and_write_node_t); + + *node = (yp_constant_path_operator_and_write_node_t) { + { + .type = YP_NODE_CONSTANT_PATH_OPERATOR_AND_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .target = target, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new ConstantPathOperatorWriteNode node. +static yp_constant_path_operator_write_node_t * +yp_constant_path_operator_write_node_create(yp_parser_t *parser, yp_constant_path_node_t *target, const yp_token_t *operator, yp_node_t *value) { + yp_constant_path_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_operator_write_node_t); + + *node = (yp_constant_path_operator_write_node_t) { + { + .type = YP_NODE_CONSTANT_PATH_OPERATOR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .target = target, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +// Allocate and initialize a new ConstantPathOperatorOrWriteNode node. +static yp_constant_path_operator_or_write_node_t * +yp_constant_path_operator_or_write_node_create(yp_parser_t *parser, yp_constant_path_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL); + yp_constant_path_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_operator_or_write_node_t); + + *node = (yp_constant_path_operator_or_write_node_t) { + { + .type = YP_NODE_CONSTANT_PATH_OPERATOR_OR_WRITE_NODE, + .location = { + .start = target->base.location.start, + .end = value->location.end + } + }, + .target = target, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new ConstantPathNode node. +static yp_constant_path_node_t * +yp_constant_path_node_create(yp_parser_t *parser, yp_node_t *parent, const yp_token_t *delimiter, yp_node_t *child) { + yp_constant_path_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_node_t); + + *node = (yp_constant_path_node_t) { + { + .type = YP_NODE_CONSTANT_PATH_NODE, + .location = { + .start = parent == NULL ? delimiter->start : parent->location.start, + .end = child->location.end + }, + }, + .parent = parent, + .child = child, + .delimiter_loc = YP_LOCATION_TOKEN_VALUE(delimiter) + }; + + return node; +} + +// Allocate a new ConstantPathWriteNode node. +static yp_constant_path_write_node_t * +yp_constant_path_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + yp_constant_path_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_path_write_node_t); + + *node = (yp_constant_path_write_node_t) { + { + .type = YP_NODE_CONSTANT_PATH_WRITE_NODE, + .location = { + .start = target->location.start, + .end = (value == NULL ? target->location.end : value->location.end) + }, + }, + .target = target, + .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new ConstantOperatorAndWriteNode node. +static yp_constant_operator_and_write_node_t * +yp_constant_operator_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(target->type == YP_NODE_CONSTANT_READ_NODE); + assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + yp_constant_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_operator_and_write_node_t); + + *node = (yp_constant_operator_and_write_node_t) { + { + .type = YP_NODE_CONSTANT_OPERATOR_AND_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new ConstantOperatorWriteNode node. +static yp_constant_operator_write_node_t * +yp_constant_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + yp_constant_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_operator_write_node_t); + + *node = (yp_constant_operator_write_node_t) { + { + .type = YP_NODE_CONSTANT_OPERATOR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +// Allocate and initialize a new ConstantOperatorOrWriteNode node. +static yp_constant_operator_or_write_node_t * +yp_constant_operator_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(target->type == YP_NODE_CONSTANT_READ_NODE); + assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL); + yp_constant_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_constant_operator_or_write_node_t); + + *node = (yp_constant_operator_or_write_node_t) { + { + .type = YP_NODE_CONSTANT_OPERATOR_OR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new ConstantReadNode node. +static yp_constant_read_node_t * +yp_constant_read_node_create(yp_parser_t *parser, const yp_token_t *name) { + assert(name->type == YP_TOKEN_CONSTANT || name->type == YP_TOKEN_MISSING); + + yp_constant_read_node_t *node = YP_ALLOC_NODE(parser, yp_constant_read_node_t); + *node = (yp_constant_read_node_t) {{ .type = YP_NODE_CONSTANT_READ_NODE, .location = YP_LOCATION_TOKEN_VALUE(name) }}; + return node; +} + +// Allocate and initialize a new DefNode node. +static yp_def_node_t * +yp_def_node_create( + yp_parser_t *parser, + const yp_token_t *name, + yp_node_t *receiver, + yp_parameters_node_t *parameters, + yp_node_t *statements, + yp_constant_id_list_t *locals, + const yp_token_t *def_keyword, + const yp_token_t *operator, + const yp_token_t *lparen, + const yp_token_t *rparen, + const yp_token_t *equal, + const yp_token_t *end_keyword +) { + yp_def_node_t *node = YP_ALLOC_NODE(parser, yp_def_node_t); + const char *end; + + if (end_keyword->type == YP_TOKEN_NOT_PROVIDED) { + end = statements->location.end; + } else { + end = end_keyword->end; + } + + *node = (yp_def_node_t) { + { + .type = YP_NODE_DEF_NODE, + .location = { .start = def_keyword->start, .end = end }, + }, + .name_loc = YP_LOCATION_TOKEN_VALUE(name), + .receiver = receiver, + .parameters = parameters, + .statements = statements, + .locals = *locals, + .def_keyword_loc = YP_LOCATION_TOKEN_VALUE(def_keyword), + .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .lparen_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(lparen), + .rparen_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(rparen), + .equal_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(equal), + .end_keyword_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +// Allocate a new DefinedNode node. +static yp_defined_node_t * +yp_defined_node_create(yp_parser_t *parser, const yp_token_t *lparen, yp_node_t *value, const yp_token_t *rparen, const yp_location_t *keyword_loc) { + yp_defined_node_t *node = YP_ALLOC_NODE(parser, yp_defined_node_t); + + *node = (yp_defined_node_t) { + { + .type = YP_NODE_DEFINED_NODE, + .location = { + .start = keyword_loc->start, + .end = (rparen->type == YP_TOKEN_NOT_PROVIDED ? value->location.end : rparen->end) + }, + }, + .lparen_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(lparen), + .value = value, + .rparen_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(rparen), + .keyword_loc = *keyword_loc + }; + + return node; +} + +// Allocate and initialize a new ElseNode node. +static yp_else_node_t * +yp_else_node_create(yp_parser_t *parser, const yp_token_t *else_keyword, yp_statements_node_t *statements, const yp_token_t *end_keyword) { + yp_else_node_t *node = YP_ALLOC_NODE(parser, yp_else_node_t); + const char *end = NULL; + if ((end_keyword->type == YP_TOKEN_NOT_PROVIDED) && (statements != NULL)) { + end = statements->base.location.end; + } else { + end = end_keyword->end; + } + + *node = (yp_else_node_t) { + { + .type = YP_NODE_ELSE_NODE, + .location = { + .start = else_keyword->start, + .end = end, + }, + }, + .else_keyword_loc = YP_LOCATION_TOKEN_VALUE(else_keyword), + .statements = statements, + .end_keyword_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +// Allocate and initialize a new EmbeddedStatementsNode node. +static yp_embedded_statements_node_t * +yp_embedded_statements_node_create(yp_parser_t *parser, const yp_token_t *opening, yp_statements_node_t *statements, const yp_token_t *closing) { + yp_embedded_statements_node_t *node = YP_ALLOC_NODE(parser, yp_embedded_statements_node_t); + + *node = (yp_embedded_statements_node_t) { + { + .type = YP_NODE_EMBEDDED_STATEMENTS_NODE, + .location = { + .start = opening->start, + .end = closing->end + } + }, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .statements = statements, + .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) + }; + + return node; +} + +// Allocate and initialize a new EmbeddedVariableNode node. +static yp_embedded_variable_node_t * +yp_embedded_variable_node_create(yp_parser_t *parser, const yp_token_t *operator, yp_node_t *variable) { + yp_embedded_variable_node_t *node = YP_ALLOC_NODE(parser, yp_embedded_variable_node_t); + + *node = (yp_embedded_variable_node_t) { + { + .type = YP_NODE_EMBEDDED_VARIABLE_NODE, + .location = { + .start = operator->start, + .end = variable->location.end + } + }, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .variable = variable + }; + + return node; +} + +// Allocate a new EnsureNode node. +static yp_ensure_node_t * +yp_ensure_node_create(yp_parser_t *parser, const yp_token_t *ensure_keyword, yp_statements_node_t *statements, const yp_token_t *end_keyword) { + yp_ensure_node_t *node = YP_ALLOC_NODE(parser, yp_ensure_node_t); + + *node = (yp_ensure_node_t) { + { + .type = YP_NODE_ENSURE_NODE, + .location = { + .start = ensure_keyword->start, + .end = end_keyword->end + }, + }, + .ensure_keyword_loc = YP_LOCATION_TOKEN_VALUE(ensure_keyword), + .statements = statements, + .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +// Allocate and initialize a new FalseNode node. +static yp_false_node_t * +yp_false_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_KEYWORD_FALSE); + yp_false_node_t *node = YP_ALLOC_NODE(parser, yp_false_node_t); + *node = (yp_false_node_t) {{ .type = YP_NODE_FALSE_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate and initialize a new find pattern node. The node list given in the +// nodes parameter is guaranteed to have at least two nodes. +static yp_find_pattern_node_t * +yp_find_pattern_node_create(yp_parser_t *parser, yp_node_list_t *nodes) { + yp_find_pattern_node_t *node = YP_ALLOC_NODE(parser, yp_find_pattern_node_t); + + yp_node_t *left = nodes->nodes[0]; + yp_node_t *right; + + if (nodes->size == 1) { + right = (yp_node_t *) yp_missing_node_create(parser, left->location.end, left->location.end); + } else { + right = nodes->nodes[nodes->size - 1]; + } + + *node = (yp_find_pattern_node_t) { + { + .type = YP_NODE_FIND_PATTERN_NODE, + .location = { + .start = left->location.start, + .end = right->location.end, + }, + }, + .constant = NULL, + .left = left, + .right = right, + .requireds = YP_EMPTY_NODE_LIST, + .opening_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + // For now we're going to just copy over each pointer manually. This could be + // much more efficient, as we could instead resize the node list to only point + // to 1...-1. + for (size_t index = 1; index < nodes->size - 1; index++) { + yp_node_list_append(&node->requireds, nodes->nodes[index]); + } + + return node; +} + +// Allocate and initialize a new FloatNode node. +static yp_float_node_t * +yp_float_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_FLOAT); + yp_float_node_t *node = YP_ALLOC_NODE(parser, yp_float_node_t); + *node = (yp_float_node_t) {{ .type = YP_NODE_FLOAT_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate and initialize a new ForNode node. +static yp_for_node_t * +yp_for_node_create( + yp_parser_t *parser, + yp_node_t *index, + yp_node_t *collection, + yp_statements_node_t *statements, + const yp_token_t *for_keyword, + const yp_token_t *in_keyword, + const yp_token_t *do_keyword, + const yp_token_t *end_keyword +) { + yp_for_node_t *node = YP_ALLOC_NODE(parser, yp_for_node_t); + + *node = (yp_for_node_t) { + { + .type = YP_NODE_FOR_NODE, + .location = { + .start = for_keyword->start, + .end = end_keyword->end + }, + }, + .index = index, + .collection = collection, + .statements = statements, + .for_keyword_loc = YP_LOCATION_TOKEN_VALUE(for_keyword), + .in_keyword_loc = YP_LOCATION_TOKEN_VALUE(in_keyword), + .do_keyword_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword), + .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +// Allocate and initialize a new ForwardingArgumentsNode node. +static yp_forwarding_arguments_node_t * +yp_forwarding_arguments_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_UDOT_DOT_DOT); + yp_forwarding_arguments_node_t *node = YP_ALLOC_NODE(parser, yp_forwarding_arguments_node_t); + *node = (yp_forwarding_arguments_node_t) {{ .type = YP_NODE_FORWARDING_ARGUMENTS_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate and initialize a new ForwardingParameterNode node. +static yp_forwarding_parameter_node_t * +yp_forwarding_parameter_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_UDOT_DOT_DOT); + yp_forwarding_parameter_node_t *node = YP_ALLOC_NODE(parser, yp_forwarding_parameter_node_t); + *node = (yp_forwarding_parameter_node_t) {{ .type = YP_NODE_FORWARDING_PARAMETER_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate and initialize a new ForwardingSuper node. +static yp_forwarding_super_node_t * +yp_forwarding_super_node_create(yp_parser_t *parser, const yp_token_t *token, yp_arguments_t *arguments) { + assert(token->type == YP_TOKEN_KEYWORD_SUPER); + yp_forwarding_super_node_t *node = YP_ALLOC_NODE(parser, yp_forwarding_super_node_t); + + *node = (yp_forwarding_super_node_t) { + { + .type = YP_NODE_FORWARDING_SUPER_NODE, + .location = { + .start = token->start, + .end = arguments->block != NULL ? arguments->block->base.location.end : token->end + }, + }, + .block = arguments->block + }; + + return node; +} + +// Allocate and initialize a new hash pattern node from an opening and closing +// token. +static yp_hash_pattern_node_t * +yp_hash_pattern_node_empty_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *closing) { + yp_hash_pattern_node_t *node = YP_ALLOC_NODE(parser, yp_hash_pattern_node_t); + + *node = (yp_hash_pattern_node_t) { + { + .type = YP_NODE_HASH_PATTERN_NODE, + .location = { + .start = opening->start, + .end = closing->end + }, + }, + .constant = NULL, + .kwrest = NULL, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_LOCATION_TOKEN_VALUE(closing), + .assocs = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Allocate and initialize a new hash pattern node. +static yp_hash_pattern_node_t * +yp_hash_pattern_node_node_list_create(yp_parser_t *parser, yp_node_list_t *assocs) { + yp_hash_pattern_node_t *node = YP_ALLOC_NODE(parser, yp_hash_pattern_node_t); + + *node = (yp_hash_pattern_node_t) { + { + .type = YP_NODE_HASH_PATTERN_NODE, + .location = { + .start = assocs->nodes[0]->location.start, + .end = assocs->nodes[assocs->size - 1]->location.end + }, + }, + .constant = NULL, + .kwrest = NULL, + .assocs = YP_EMPTY_NODE_LIST, + .opening_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .closing_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + for (size_t index = 0; index < assocs->size; index++) { + yp_node_t *assoc = assocs->nodes[index]; + yp_node_list_append(&node->assocs, assoc); + } + + return node; +} + +// Allocate and initialize a new GlobalVariableOperatorAndWriteNode node. +static yp_global_variable_operator_and_write_node_t * +yp_global_variable_operator_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(target->type == YP_NODE_GLOBAL_VARIABLE_READ_NODE); + assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + yp_global_variable_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_operator_and_write_node_t); + + *node = (yp_global_variable_operator_and_write_node_t) { + { + .type = YP_NODE_GLOBAL_VARIABLE_OPERATOR_AND_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new GlobalVariableOperatorWriteNode node. +static yp_global_variable_operator_write_node_t * +yp_global_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + yp_global_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_operator_write_node_t); + + *node = (yp_global_variable_operator_write_node_t) { + { + .type = YP_NODE_GLOBAL_VARIABLE_OPERATOR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +// Allocate and initialize a new GlobalVariableOperatorOrWriteNode node. +static yp_global_variable_operator_or_write_node_t * +yp_global_variable_operator_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(target->type == YP_NODE_GLOBAL_VARIABLE_READ_NODE); + assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL); + yp_global_variable_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_operator_or_write_node_t); + + *node = (yp_global_variable_operator_or_write_node_t) { + { + .type = YP_NODE_GLOBAL_VARIABLE_OPERATOR_OR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate a new GlobalVariableReadNode node. +static yp_global_variable_read_node_t * +yp_global_variable_read_node_create(yp_parser_t *parser, const yp_token_t *name) { + yp_global_variable_read_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_read_node_t); + + *node = (yp_global_variable_read_node_t) { + { + .type = YP_NODE_GLOBAL_VARIABLE_READ_NODE, + .location = YP_LOCATION_TOKEN_VALUE(name), + } + }; + + return node; +} + +// Allocate a new GlobalVariableWriteNode node. +static yp_global_variable_write_node_t * +yp_global_variable_write_node_create(yp_parser_t *parser, const yp_location_t *name_loc, const yp_token_t *operator, yp_node_t *value) { + yp_global_variable_write_node_t *node = YP_ALLOC_NODE(parser, yp_global_variable_write_node_t); + + *node = (yp_global_variable_write_node_t) { + { + .type = YP_NODE_GLOBAL_VARIABLE_WRITE_NODE, + .location = { + .start = name_loc->start, + .end = (value == NULL ? name_loc->end : value->location.end) + }, + }, + .name_loc = *name_loc, + .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate a new HashNode node. +static yp_hash_node_t * +yp_hash_node_create(yp_parser_t *parser, const yp_token_t *opening) { + assert(opening != NULL); + yp_hash_node_t *node = YP_ALLOC_NODE(parser, yp_hash_node_t); + + *node = (yp_hash_node_t) { + { + .type = YP_NODE_HASH_NODE, + .location = { + .start = opening->start, + .end = opening->end + }, + }, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_LOCATION_NULL_VALUE(parser), + .elements = YP_EMPTY_NODE_LIST + }; + + return node; +} + +static inline void +yp_hash_node_elements_append(yp_hash_node_t *hash, yp_node_t *element) { + yp_node_list_append(&hash->elements, element); +} + +static inline void +yp_hash_node_closing_loc_set(yp_hash_node_t *hash, yp_token_t *token) { + hash->base.location.end = token->end; + hash->closing_loc = YP_LOCATION_TOKEN_VALUE(token); +} + +// Allocate a new IfNode node. +static yp_if_node_t * +yp_if_node_create(yp_parser_t *parser, + const yp_token_t *if_keyword, + yp_node_t *predicate, + yp_statements_node_t *statements, + yp_node_t *consequent, + const yp_token_t *end_keyword +) { + yp_if_node_t *node = YP_ALLOC_NODE(parser, yp_if_node_t); + + const char *end; + if (end_keyword->type != YP_TOKEN_NOT_PROVIDED) { + end = end_keyword->end; + } else if (consequent != NULL) { + end = consequent->location.end; + } else if ((statements != NULL) && (statements->body.size != 0)) { + end = statements->base.location.end; + } else { + end = predicate->location.end; + } + + *node = (yp_if_node_t) { + { + .type = YP_NODE_IF_NODE, + .location = { + .start = if_keyword->start, + .end = end + }, + }, + .if_keyword_loc = YP_LOCATION_TOKEN_VALUE(if_keyword), + .predicate = predicate, + .statements = statements, + .consequent = consequent, + .end_keyword_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +// Allocate and initialize new IfNode node in the modifier form. +static yp_if_node_t * +yp_if_node_modifier_create(yp_parser_t *parser, yp_node_t *statement, const yp_token_t *if_keyword, yp_node_t *predicate) { + yp_if_node_t *node = YP_ALLOC_NODE(parser, yp_if_node_t); + + yp_statements_node_t *statements = yp_statements_node_create(parser); + yp_statements_node_body_append(statements, statement); + + *node = (yp_if_node_t) { + { + .type = YP_NODE_IF_NODE, + .location = { + .start = statement->location.start, + .end = predicate->location.end + }, + }, + .if_keyword_loc = YP_LOCATION_TOKEN_VALUE(if_keyword), + .predicate = predicate, + .statements = statements, + .consequent = NULL, + .end_keyword_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +// Allocate and initialize an if node from a ternary expression. +static yp_if_node_t * +yp_if_node_ternary_create(yp_parser_t *parser, yp_node_t *predicate, yp_node_t *true_expression, const yp_token_t *colon, yp_node_t *false_expression) { + yp_statements_node_t *if_statements = yp_statements_node_create(parser); + yp_statements_node_body_append(if_statements, true_expression); + + yp_statements_node_t *else_statements = yp_statements_node_create(parser); + yp_statements_node_body_append(else_statements, false_expression); + + yp_token_t end_keyword = not_provided(parser); + yp_else_node_t *else_node = yp_else_node_create(parser, colon, else_statements, &end_keyword); + + yp_if_node_t *node = YP_ALLOC_NODE(parser, yp_if_node_t); + + *node = (yp_if_node_t) { + { + .type = YP_NODE_IF_NODE, + .location = { + .start = predicate->location.start, + .end = false_expression->location.end, + }, + }, + .if_keyword_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .predicate = predicate, + .statements = if_statements, + .consequent = (yp_node_t *)else_node, + .end_keyword_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; + +} + +static inline void +yp_if_node_end_keyword_loc_set(yp_if_node_t *node, const yp_token_t *keyword) { + node->base.location.end = keyword->end; + node->end_keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword); +} + +static inline void +yp_else_node_end_keyword_loc_set(yp_else_node_t *node, const yp_token_t *keyword) { + node->base.location.end = keyword->end; + node->end_keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword); +} + +// Allocate and initialize a new IntegerNode node. +static yp_integer_node_t * +yp_integer_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_INTEGER); + yp_integer_node_t *node = YP_ALLOC_NODE(parser, yp_integer_node_t); + *node = (yp_integer_node_t) {{ .type = YP_NODE_INTEGER_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate and initialize a new RationalNode node. +static yp_rational_node_t * +yp_rational_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_RATIONAL_NUMBER); + assert(parser->lex_modes.current->mode == YP_LEX_NUMERIC); + + yp_node_t *numeric_node; + yp_token_t numeric_token = { + .type = parser->lex_modes.current->as.numeric.type, + .start = parser->lex_modes.current->as.numeric.start, + .end = parser->lex_modes.current->as.numeric.end + }; + switch (parser->lex_modes.current->as.numeric.type) { + case YP_TOKEN_INTEGER: { + lex_mode_pop(parser); + numeric_node = (yp_node_t *)yp_integer_node_create(parser, &numeric_token); + break; + } + case YP_TOKEN_FLOAT: { + lex_mode_pop(parser); + numeric_node = (yp_node_t *)yp_float_node_create(parser, &numeric_token); + break; + } + default: { + lex_mode_pop(parser); + numeric_node = (yp_node_t *)yp_missing_node_create(parser, numeric_token.start, numeric_token.end); + (void)numeric_node; // Suppress clang-analyzer-deadcode.DeadStores warning + assert(false && "unreachable"); + } + } + + yp_rational_node_t *node = YP_ALLOC_NODE(parser, yp_rational_node_t); + + *node = (yp_rational_node_t) { + { .type = YP_NODE_RATIONAL_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }, + .numeric = numeric_node, + }; + assert(parser->lex_modes.current->mode != YP_LEX_NUMERIC); + return node; +} + +// Allocate and initialize a new ImaginaryNode node. +static yp_imaginary_node_t * +yp_imaginary_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_IMAGINARY_NUMBER); + assert(parser->lex_modes.current->mode == YP_LEX_NUMERIC); + + yp_node_t *numeric_node; + yp_token_t numeric_token = { + .type = parser->lex_modes.current->as.numeric.type, + .start = parser->lex_modes.current->as.numeric.start, + .end = parser->lex_modes.current->as.numeric.end + }; + switch (parser->lex_modes.current->as.numeric.type) { + case YP_TOKEN_INTEGER: { + lex_mode_pop(parser); + numeric_node = (yp_node_t *)yp_integer_node_create(parser, &numeric_token); + break; + } + case YP_TOKEN_FLOAT: { + lex_mode_pop(parser); + numeric_node = (yp_node_t *)yp_float_node_create(parser, &numeric_token); + break; + } + case YP_TOKEN_RATIONAL_NUMBER: { + lex_mode_pop(parser); + numeric_node = (yp_node_t *)yp_rational_node_create(parser, &numeric_token); + break; + } + default: { + lex_mode_pop(parser); + numeric_node = (yp_node_t *)yp_missing_node_create(parser, numeric_token.start, numeric_token.end); + (void)numeric_node; // Suppress clang-analyzer-deadcode.DeadStores warning + assert(false && "unreachable"); + } + } + + yp_imaginary_node_t *node = YP_ALLOC_NODE(parser, yp_imaginary_node_t); + + *node = (yp_imaginary_node_t) { + { .type = YP_NODE_IMAGINARY_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }, + .numeric = numeric_node + }; + assert(parser->lex_modes.current->mode != YP_LEX_NUMERIC); + return node; +} + +// Allocate and initialize a new InNode node. +static yp_in_node_t * +yp_in_node_create(yp_parser_t *parser, yp_node_t *pattern, yp_statements_node_t *statements, const yp_token_t *in_keyword, const yp_token_t *then_keyword) { + yp_in_node_t *node = YP_ALLOC_NODE(parser, yp_in_node_t); + + const char *end; + if (statements != NULL) { + end = statements->base.location.end; + } else if (then_keyword->type != YP_TOKEN_NOT_PROVIDED) { + end = then_keyword->end; + } else { + end = pattern->location.end; + } + + *node = (yp_in_node_t) { + { + .type = YP_NODE_IN_NODE, + .location = { + .start = in_keyword->start, + .end = end + }, + }, + .pattern = pattern, + .statements = statements, + .in_loc = YP_LOCATION_TOKEN_VALUE(in_keyword), + .then_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword) + }; + + return node; +} + +// Allocate and initialize a new InstanceVariableOperatorAndWriteNode node. +static yp_instance_variable_operator_and_write_node_t * +yp_instance_variable_operator_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(target->type == YP_NODE_INSTANCE_VARIABLE_READ_NODE); + assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + yp_instance_variable_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_operator_and_write_node_t); + + *node = (yp_instance_variable_operator_and_write_node_t) { + { + .type = YP_NODE_INSTANCE_VARIABLE_OPERATOR_AND_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new InstanceVariableOperatorWriteNode node. +static yp_instance_variable_operator_write_node_t * +yp_instance_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + yp_instance_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_operator_write_node_t); + + *node = (yp_instance_variable_operator_write_node_t) { + { + .type = YP_NODE_INSTANCE_VARIABLE_OPERATOR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value, + .operator = yp_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +// Allocate and initialize a new InstanceVariableOperatorOrWriteNode node. +static yp_instance_variable_operator_or_write_node_t * +yp_instance_variable_operator_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { + assert(target->type == YP_NODE_INSTANCE_VARIABLE_READ_NODE); + assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL); + yp_instance_variable_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_operator_or_write_node_t); + + *node = (yp_instance_variable_operator_or_write_node_t) { + { + .type = YP_NODE_INSTANCE_VARIABLE_OPERATOR_OR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new InstanceVariableReadNode node. +static yp_instance_variable_read_node_t * +yp_instance_variable_read_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_INSTANCE_VARIABLE); + yp_instance_variable_read_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_read_node_t); + + *node = (yp_instance_variable_read_node_t) {{ + .type = YP_NODE_INSTANCE_VARIABLE_READ_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) + }}; + + return node; +} + +// Initialize a new InstanceVariableWriteNode node from an InstanceVariableRead node. +static yp_instance_variable_write_node_t * +yp_instance_variable_write_node_create(yp_parser_t *parser, yp_instance_variable_read_node_t *read_node, yp_token_t *operator, yp_node_t *value) { + yp_instance_variable_write_node_t *node = YP_ALLOC_NODE(parser, yp_instance_variable_write_node_t); + *node = (yp_instance_variable_write_node_t) { + { + .type = YP_NODE_INSTANCE_VARIABLE_WRITE_NODE, + .location = { + .start = read_node->base.location.start, + .end = value == NULL ? read_node->base.location.end : value->location.end + } + }, + .name_loc = YP_LOCATION_NODE_BASE_VALUE(read_node), + .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate a new InterpolatedRegularExpressionNode node. +static yp_interpolated_regular_expression_node_t * +yp_interpolated_regular_expression_node_create(yp_parser_t *parser, const yp_token_t *opening) { + yp_interpolated_regular_expression_node_t *node = YP_ALLOC_NODE(parser, yp_interpolated_regular_expression_node_t); + + *node = (yp_interpolated_regular_expression_node_t) { + { + .type = YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE, + .location = { + .start = opening->start, + .end = NULL, + }, + }, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_LOCATION_TOKEN_VALUE(opening), + .flags = 0, + .parts = YP_EMPTY_NODE_LIST + }; + + return node; +} + +static inline void +yp_interpolated_regular_expression_node_append(yp_interpolated_regular_expression_node_t *node, yp_node_t *part) { + yp_node_list_append(&node->parts, part); + node->base.location.end = part->location.end; +} + +static inline void +yp_interpolated_regular_expression_node_closing_set(yp_interpolated_regular_expression_node_t *node, const yp_token_t *closing) { + node->closing_loc = YP_LOCATION_TOKEN_VALUE(closing); + node->base.location.end = closing->end; + node->flags = yp_regular_expression_flags_create(closing); +} + +// Allocate and initialize a new InterpolatedStringNode node. +static yp_interpolated_string_node_t * +yp_interpolated_string_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_node_list_t *parts, const yp_token_t *closing) { + yp_interpolated_string_node_t *node = YP_ALLOC_NODE(parser, yp_interpolated_string_node_t); + + *node = (yp_interpolated_string_node_t) { + { + .type = YP_NODE_INTERPOLATED_STRING_NODE, + .location = { + .start = opening->start, + .end = closing->end, + }, + }, + .opening_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .parts = parts == NULL ? YP_EMPTY_NODE_LIST : *parts + }; + + return node; +} + +// Append a part to an InterpolatedStringNode node. +static inline void +yp_interpolated_string_node_append(yp_interpolated_string_node_t *node, yp_node_t *part) { + yp_node_list_append(&node->parts, part); + node->base.location.end = part->location.end; +} + +// Set the closing token of the given InterpolatedStringNode node. +static void +yp_interpolated_string_node_closing_set(yp_interpolated_string_node_t *node, const yp_token_t *closing) { + node->closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing); + node->base.location.end = closing->end; +} + +// Allocate and initialize a new InterpolatedSymbolNode node. +static yp_interpolated_symbol_node_t * +yp_interpolated_symbol_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_node_list_t *parts, const yp_token_t *closing) { + yp_interpolated_symbol_node_t *node = YP_ALLOC_NODE(parser, yp_interpolated_symbol_node_t); + + *node = (yp_interpolated_symbol_node_t) { + { + .type = YP_NODE_INTERPOLATED_SYMBOL_NODE, + .location = { + .start = opening->start, + .end = closing->end, + }, + }, + .opening_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .parts = parts == NULL ? YP_EMPTY_NODE_LIST : *parts + }; + + return node; +} + +static inline void +yp_interpolated_symbol_node_append(yp_interpolated_symbol_node_t *node, yp_node_t *part) { + yp_node_list_append(&node->parts, part); + if (!node->base.location.start) { + node->base.location.start = part->location.start; + } + node->base.location.end = part->location.end; +} + +static inline void +yp_interpolated_symbol_node_closing_set(yp_interpolated_symbol_node_t *node, const yp_token_t *closing) { + node->closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing); + node->base.location.end = closing->end; +} + +// Allocate a new InterpolatedXStringNode node. +static yp_interpolated_x_string_node_t * +yp_interpolated_xstring_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *closing) { + yp_interpolated_x_string_node_t *node = YP_ALLOC_NODE(parser, yp_interpolated_x_string_node_t); + + *node = (yp_interpolated_x_string_node_t) { + { + .type = YP_NODE_INTERPOLATED_X_STRING_NODE, + .location = { + .start = opening->start, + .end = closing->end + }, + }, + .opening_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .parts = YP_EMPTY_NODE_LIST + }; + + return node; +} + +static inline void +yp_interpolated_xstring_node_append(yp_interpolated_x_string_node_t *node, yp_node_t *part) { + yp_node_list_append(&node->parts, part); + node->base.location.end = part->location.end; +} + +static inline void +yp_interpolated_xstring_node_closing_set(yp_interpolated_x_string_node_t *node, const yp_token_t *closing) { + node->closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing); + node->base.location.end = closing->end; +} + +// Allocate a new KeywordHashNode node. +static yp_keyword_hash_node_t * +yp_keyword_hash_node_create(yp_parser_t *parser) { + yp_keyword_hash_node_t *node = YP_ALLOC_NODE(parser, yp_keyword_hash_node_t); + + *node = (yp_keyword_hash_node_t) { + .base = { + .type = YP_NODE_KEYWORD_HASH_NODE, + .location = { + .start = NULL, + .end = NULL + }, + }, + .elements = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Append an element to a KeywordHashNode node. +static void +yp_keyword_hash_node_elements_append(yp_keyword_hash_node_t *hash, yp_node_t *element) { + yp_node_list_append(&hash->elements, element); + if (hash->base.location.start == NULL) { + hash->base.location.start = element->location.start; + } + hash->base.location.end = element->location.end; +} + +// Allocate a new KeywordParameterNode node. +static yp_keyword_parameter_node_t * +yp_keyword_parameter_node_create(yp_parser_t *parser, const yp_token_t *name, yp_node_t *value) { + yp_keyword_parameter_node_t *node = YP_ALLOC_NODE(parser, yp_keyword_parameter_node_t); + + *node = (yp_keyword_parameter_node_t) { + { + .type = YP_NODE_KEYWORD_PARAMETER_NODE, + .location = { + .start = name->start, + .end = value == NULL ? name->end : value->location.end + }, + }, + .name_loc = YP_LOCATION_TOKEN_VALUE(name), + .value = value + }; + + return node; +} + +// Allocate a new KeywordRestParameterNode node. +static yp_keyword_rest_parameter_node_t * +yp_keyword_rest_parameter_node_create(yp_parser_t *parser, const yp_token_t *operator, const yp_token_t *name) { + yp_keyword_rest_parameter_node_t *node = YP_ALLOC_NODE(parser, yp_keyword_rest_parameter_node_t); + + *node = (yp_keyword_rest_parameter_node_t) { + { + .type = YP_NODE_KEYWORD_REST_PARAMETER_NODE, + .location = { + .start = operator->start, + .end = (name->type == YP_TOKEN_NOT_PROVIDED ? operator->end : name->end) + }, + }, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .name_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(name) + }; + + return node; +} + +// Allocate a new LambdaNode node. +static yp_lambda_node_t * +yp_lambda_node_create( + yp_parser_t *parser, + yp_constant_id_list_t *locals, + const yp_token_t *opening, + yp_block_parameters_node_t *parameters, + yp_node_t *statements, + const yp_token_t *closing +) { + yp_lambda_node_t *node = YP_ALLOC_NODE(parser, yp_lambda_node_t); + + *node = (yp_lambda_node_t) { + { + .type = YP_NODE_LAMBDA_NODE, + .location = { + .start = opening->start, + .end = closing->end + }, + }, + .locals = *locals, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .parameters = parameters, + .statements = statements + }; + + return node; +} + +// Allocate and initialize a new LocalVariableOperatorAndWriteNode node. +static yp_local_variable_operator_and_write_node_t * +yp_local_variable_operator_and_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value, yp_constant_id_t constant_id) { + assert(target->type == YP_NODE_LOCAL_VARIABLE_READ_NODE || target->type == YP_NODE_CALL_NODE); + assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + yp_local_variable_operator_and_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_operator_and_write_node_t); + + *node = (yp_local_variable_operator_and_write_node_t) { + { + .type = YP_NODE_LOCAL_VARIABLE_OPERATOR_AND_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value, + .constant_id = constant_id + }; + + return node; +} + +// Allocate and initialize a new LocalVariableOperatorWriteNode node. +static yp_local_variable_operator_write_node_t * +yp_local_variable_operator_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value, yp_constant_id_t constant_id) { + yp_local_variable_operator_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_operator_write_node_t); + + *node = (yp_local_variable_operator_write_node_t) { + { + .type = YP_NODE_LOCAL_VARIABLE_OPERATOR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value, + .constant_id = constant_id, + .operator_id = yp_parser_constant_id_location(parser, operator->start, operator->end - 1) + }; + + return node; +} + +// Allocate and initialize a new LocalVariableOperatorOrWriteNode node. +static yp_local_variable_operator_or_write_node_t * +yp_local_variable_operator_or_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value, yp_constant_id_t constant_id) { + assert(target->type == YP_NODE_LOCAL_VARIABLE_READ_NODE || target->type == YP_NODE_CALL_NODE); + assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL); + yp_local_variable_operator_or_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_operator_or_write_node_t); + + *node = (yp_local_variable_operator_or_write_node_t) { + { + .type = YP_NODE_LOCAL_VARIABLE_OPERATOR_OR_WRITE_NODE, + .location = { + .start = target->location.start, + .end = value->location.end + } + }, + .name_loc = target->location, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value, + .constant_id = constant_id + }; + + return node; +} + +// Allocate a new LocalVariableReadNode node. +static yp_local_variable_read_node_t * +yp_local_variable_read_node_create(yp_parser_t *parser, const yp_token_t *name, uint32_t depth) { + yp_local_variable_read_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_read_node_t); + + *node = (yp_local_variable_read_node_t) { + { + .type = YP_NODE_LOCAL_VARIABLE_READ_NODE, + .location = YP_LOCATION_TOKEN_VALUE(name) + }, + .constant_id = yp_parser_constant_id_token(parser, name), + .depth = depth + }; + + return node; +} + +// Allocate and initialize a new LocalVariableWriteNode node. +static yp_local_variable_write_node_t * +yp_local_variable_write_node_create(yp_parser_t *parser, yp_constant_id_t constant_id, uint32_t depth, yp_node_t *value, const yp_location_t *name_loc, const yp_token_t *operator) { + yp_local_variable_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_write_node_t); + + *node = (yp_local_variable_write_node_t) { + { + .type = YP_NODE_LOCAL_VARIABLE_WRITE_NODE, + .location = { + .start = name_loc->start, + .end = value == NULL ? name_loc->end : value->location.end + } + }, + .constant_id = constant_id, + .depth = depth, + .value = value, + .name_loc = *name_loc, + .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +// Allocate and initialize a new LocalVariableWriteNode node without an operator or target. +static yp_local_variable_write_node_t * +yp_local_variable_target_node_create(yp_parser_t *parser, const yp_token_t *name) { + yp_local_variable_write_node_t *node = YP_ALLOC_NODE(parser, yp_local_variable_write_node_t); + + *node = (yp_local_variable_write_node_t) { + { + .type = YP_NODE_LOCAL_VARIABLE_WRITE_NODE, + .location = YP_LOCATION_TOKEN_VALUE(name) + }, + .constant_id = yp_parser_constant_id_token(parser, name), + .depth = 0, + .value = NULL, + .name_loc = YP_LOCATION_TOKEN_VALUE(name), + .operator_loc = { .start = NULL, .end = NULL } + }; + + return node; +} + +// Allocate and initialize a new MatchPredicateNode node. +static yp_match_predicate_node_t * +yp_match_predicate_node_create(yp_parser_t *parser, yp_node_t *value, yp_node_t *pattern, const yp_token_t *operator) { + yp_match_predicate_node_t *node = YP_ALLOC_NODE(parser, yp_match_predicate_node_t); + + *node = (yp_match_predicate_node_t) { + { + .type = YP_NODE_MATCH_PREDICATE_NODE, + .location = { + .start = value->location.start, + .end = pattern->location.end + } + }, + .value = value, + .pattern = pattern, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +// Allocate and initialize a new MatchRequiredNode node. +static yp_match_required_node_t * +yp_match_required_node_create(yp_parser_t *parser, yp_node_t *value, yp_node_t *pattern, const yp_token_t *operator) { + yp_match_required_node_t *node = YP_ALLOC_NODE(parser, yp_match_required_node_t); + + *node = (yp_match_required_node_t) { + { + .type = YP_NODE_MATCH_REQUIRED_NODE, + .location = { + .start = value->location.start, + .end = pattern->location.end + } + }, + .value = value, + .pattern = pattern, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +// Allocate a new ModuleNode node. +static yp_module_node_t * +yp_module_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const yp_token_t *module_keyword, yp_node_t *constant_path, yp_node_t *statements, const yp_token_t *end_keyword) { + yp_module_node_t *node = YP_ALLOC_NODE(parser, yp_module_node_t); + + *node = (yp_module_node_t) { + { + .type = YP_NODE_MODULE_NODE, + .location = { + .start = module_keyword->start, + .end = end_keyword->end + } + }, + .locals = (locals == NULL ? ((yp_constant_id_list_t) { .ids = NULL, .size = 0, .capacity = 0 }) : *locals), + .module_keyword_loc = YP_LOCATION_TOKEN_VALUE(module_keyword), + .constant_path = constant_path, + .statements = statements, + .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +// Allocate a new MultiWriteNode node. +static yp_multi_write_node_t * +yp_multi_write_node_create(yp_parser_t *parser, const yp_token_t *operator, yp_node_t *value, const yp_location_t *lparen_loc, const yp_location_t *rparen_loc) { + yp_multi_write_node_t *node = YP_ALLOC_NODE(parser, yp_multi_write_node_t); + + *node = (yp_multi_write_node_t) { + { + .type = YP_NODE_MULTI_WRITE_NODE, + .location = { .start = NULL, .end = NULL }, + }, + .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), + .value = value, + .lparen_loc = *lparen_loc, + .rparen_loc = *rparen_loc, + .targets = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Append a target to a MultiWriteNode node. +static void +yp_multi_write_node_targets_append(yp_multi_write_node_t *node, yp_node_t *target) { + yp_node_list_append(&node->targets, target); + + if (node->base.location.start == NULL || (node->base.location.start > target->location.start)) { + node->base.location.start = target->location.start; + } + + if (node->base.location.end == NULL || (node->base.location.end < target->location.end)) { + node->base.location.end = target->location.end; + } +} + +static inline void +yp_multi_write_node_operator_loc_set(yp_multi_write_node_t *node, const yp_token_t *operator) { + node->operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator); +} + +// Allocate and initialize a new NextNode node. +static yp_next_node_t * +yp_next_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_arguments_node_t *arguments) { + assert(keyword->type == YP_TOKEN_KEYWORD_NEXT); + yp_next_node_t *node = YP_ALLOC_NODE(parser, yp_next_node_t); + + *node = (yp_next_node_t) { + { + .type = YP_NODE_NEXT_NODE, + .location = { + .start = keyword->start, + .end = (arguments == NULL ? keyword->end : arguments->base.location.end) + } + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .arguments = arguments + }; + + return node; +} + +// Allocate and initialize a new NilNode node. +static yp_nil_node_t * +yp_nil_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_KEYWORD_NIL); + yp_nil_node_t *node = YP_ALLOC_NODE(parser, yp_nil_node_t); + + *node = (yp_nil_node_t) {{ .type = YP_NODE_NIL_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate and initialize a new NoKeywordsParameterNode node. +static yp_no_keywords_parameter_node_t * +yp_no_keywords_parameter_node_create(yp_parser_t *parser, const yp_token_t *operator, const yp_token_t *keyword) { + assert(operator->type == YP_TOKEN_USTAR_STAR); + assert(keyword->type == YP_TOKEN_KEYWORD_NIL); + yp_no_keywords_parameter_node_t *node = YP_ALLOC_NODE(parser, yp_no_keywords_parameter_node_t); + + *node = (yp_no_keywords_parameter_node_t) { + { + .type = YP_NODE_NO_KEYWORDS_PARAMETER_NODE, + .location = { + .start = operator->start, + .end = keyword->end + } + }, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword) + }; + + return node; +} + +// Allocate a new NthReferenceReadNode node. +static yp_numbered_reference_read_node_t * +yp_numbered_reference_read_node_create(yp_parser_t *parser, const yp_token_t *name) { + assert(name->type == YP_TOKEN_NUMBERED_REFERENCE); + yp_numbered_reference_read_node_t *node = YP_ALLOC_NODE(parser, yp_numbered_reference_read_node_t); + + *node = (yp_numbered_reference_read_node_t) { + { + .type = YP_NODE_NUMBERED_REFERENCE_READ_NODE, + .location = YP_LOCATION_TOKEN_VALUE(name), + } + }; + + return node; +} + +// Allocate a new OptionalParameterNode node. +static yp_optional_parameter_node_t * +yp_optional_parameter_node_create(yp_parser_t *parser, const yp_token_t *name, const yp_token_t *operator, yp_node_t *value) { + yp_optional_parameter_node_t *node = YP_ALLOC_NODE(parser, yp_optional_parameter_node_t); + + *node = (yp_optional_parameter_node_t) { + { + .type = YP_NODE_OPTIONAL_PARAMETER_NODE, + .location = { + .start = name->start, + .end = value->location.end + } + }, + .constant_id = yp_parser_constant_id_token(parser, name), + .name_loc = YP_LOCATION_TOKEN_VALUE(name), + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .value = value + }; + + return node; +} + +// Allocate and initialize a new OrNode node. +static yp_or_node_t * +yp_or_node_create(yp_parser_t *parser, yp_node_t *left, const yp_token_t *operator, yp_node_t *right) { + yp_or_node_t *node = YP_ALLOC_NODE(parser, yp_or_node_t); + + *node = (yp_or_node_t) { + { + .type = YP_NODE_OR_NODE, + .location = { + .start = left->location.start, + .end = right->location.end + } + }, + .left = left, + .right = right, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +// Allocate and initialize a new ParametersNode node. +static yp_parameters_node_t * +yp_parameters_node_create(yp_parser_t *parser) { + yp_parameters_node_t *node = YP_ALLOC_NODE(parser, yp_parameters_node_t); + + *node = (yp_parameters_node_t) { + { + .type = YP_NODE_PARAMETERS_NODE, + .location = { .start = parser->current.start, .end = parser->current.start }, + }, + .rest = NULL, + .keyword_rest = NULL, + .block = NULL, + .requireds = YP_EMPTY_NODE_LIST, + .optionals = YP_EMPTY_NODE_LIST, + .posts = YP_EMPTY_NODE_LIST, + .keywords = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Set the location properly for the parameters node. +static void +yp_parameters_node_location_set(yp_parameters_node_t *params, yp_node_t *param) { + if (params->base.location.start == NULL) { + params->base.location.start = param->location.start; + } else { + params->base.location.start = params->base.location.start < param->location.start ? params->base.location.start : param->location.start; + } + + if (params->base.location.end == NULL) { + params->base.location.end = param->location.end; + } else { + params->base.location.end = params->base.location.end > param->location.end ? params->base.location.end : param->location.end; + } +} + +// Append a required parameter to a ParametersNode node. +static void +yp_parameters_node_requireds_append(yp_parameters_node_t *params, yp_node_t *param) { + yp_parameters_node_location_set(params, param); + yp_node_list_append(¶ms->requireds, param); +} + +// Append an optional parameter to a ParametersNode node. +static void +yp_parameters_node_optionals_append(yp_parameters_node_t *params, yp_optional_parameter_node_t *param) { + yp_parameters_node_location_set(params, (yp_node_t *) param); + yp_node_list_append(¶ms->optionals, (yp_node_t *) param); +} + +// Append a post optional arguments parameter to a ParametersNode node. +static void +yp_parameters_node_posts_append(yp_parameters_node_t *params, yp_node_t *param) { + yp_parameters_node_location_set(params, param); + yp_node_list_append(¶ms->posts, param); +} + +// Set the rest parameter on a ParametersNode node. +static void +yp_parameters_node_rest_set(yp_parameters_node_t *params, yp_rest_parameter_node_t *param) { + yp_parameters_node_location_set(params, (yp_node_t *) param); + params->rest = param; +} + +// Append a keyword parameter to a ParametersNode node. +static void +yp_parameters_node_keywords_append(yp_parameters_node_t *params, yp_node_t *param) { + yp_parameters_node_location_set(params, param); + yp_node_list_append(¶ms->keywords, param); +} + +// Set the keyword rest parameter on a ParametersNode node. +static void +yp_parameters_node_keyword_rest_set(yp_parameters_node_t *params, yp_node_t *param) { + yp_parameters_node_location_set(params, param); + params->keyword_rest = param; +} + +// Set the block parameter on a ParametersNode node. +static void +yp_parameters_node_block_set(yp_parameters_node_t *params, yp_block_parameter_node_t *param) { + yp_parameters_node_location_set(params, (yp_node_t *) param); + params->block = param; +} + +// Allocate a new ProgramNode node. +static yp_program_node_t * +yp_program_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, yp_statements_node_t *statements) { + yp_program_node_t *node = YP_ALLOC_NODE(parser, yp_program_node_t); + + *node = (yp_program_node_t) { + { + .type = YP_NODE_PROGRAM_NODE, + .location = { + .start = statements == NULL ? parser->start : statements->base.location.start, + .end = statements == NULL ? parser->end : statements->base.location.end + } + }, + .locals = *locals, + .statements = statements + }; + + return node; +} + +// Allocate and initialize new ParenthesesNode node. +static yp_parentheses_node_t * +yp_parentheses_node_create(yp_parser_t *parser, const yp_token_t *opening, yp_node_t *statements, const yp_token_t *closing) { + yp_parentheses_node_t *node = YP_ALLOC_NODE(parser, yp_parentheses_node_t); + + *node = (yp_parentheses_node_t) { + { + .type = YP_NODE_PARENTHESES_NODE, + .location = { + .start = opening->start, + .end = closing->end + } + }, + .statements = statements, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) + }; + + return node; +} + +// Allocate and initialize a new PinnedExpressionNode node. +static yp_pinned_expression_node_t * +yp_pinned_expression_node_create(yp_parser_t *parser, yp_node_t *expression, const yp_token_t *operator, const yp_token_t *lparen, const yp_token_t *rparen) { + yp_pinned_expression_node_t *node = YP_ALLOC_NODE(parser, yp_pinned_expression_node_t); + + *node = (yp_pinned_expression_node_t) { + { + .type = YP_NODE_PINNED_EXPRESSION_NODE, + .location = { + .start = operator->start, + .end = rparen->end + } + }, + .expression = expression, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .lparen_loc = YP_LOCATION_TOKEN_VALUE(lparen), + .rparen_loc = YP_LOCATION_TOKEN_VALUE(rparen) + }; + + return node; +} + +// Allocate and initialize a new PinnedVariableNode node. +static yp_pinned_variable_node_t * +yp_pinned_variable_node_create(yp_parser_t *parser, const yp_token_t *operator, yp_node_t *variable) { + yp_pinned_variable_node_t *node = YP_ALLOC_NODE(parser, yp_pinned_variable_node_t); + + *node = (yp_pinned_variable_node_t) { + { + .type = YP_NODE_PINNED_VARIABLE_NODE, + .location = { + .start = operator->start, + .end = variable->location.end + } + }, + .variable = variable, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) + }; + + return node; +} + +// Allocate and initialize a new PostExecutionNode node. +static yp_post_execution_node_t * +yp_post_execution_node_create(yp_parser_t *parser, const yp_token_t *keyword, const yp_token_t *opening, yp_statements_node_t *statements, const yp_token_t *closing) { + yp_post_execution_node_t *node = YP_ALLOC_NODE(parser, yp_post_execution_node_t); + + *node = (yp_post_execution_node_t) { + { + .type = YP_NODE_POST_EXECUTION_NODE, + .location = { + .start = keyword->start, + .end = closing->end + } + }, + .statements = statements, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) + }; + + return node; +} + +// Allocate and initialize a new PreExecutionNode node. +static yp_pre_execution_node_t * +yp_pre_execution_node_create(yp_parser_t *parser, const yp_token_t *keyword, const yp_token_t *opening, yp_statements_node_t *statements, const yp_token_t *closing) { + yp_pre_execution_node_t *node = YP_ALLOC_NODE(parser, yp_pre_execution_node_t); + + *node = (yp_pre_execution_node_t) { + { + .type = YP_NODE_PRE_EXECUTION_NODE, + .location = { + .start = keyword->start, + .end = closing->end + } + }, + .statements = statements, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) + }; + + return node; +} + +// Allocate and initialize new RangeNode node. +static yp_range_node_t * +yp_range_node_create(yp_parser_t *parser, yp_node_t *left, const yp_token_t *operator, yp_node_t *right) { + yp_range_node_t *node = YP_ALLOC_NODE(parser, yp_range_node_t); + + *node = (yp_range_node_t) { + { + .type = YP_NODE_RANGE_NODE, + .location = { + .start = (left == NULL ? operator->start : left->location.start), + .end = (right == NULL ? operator->end : right->location.end) + } + }, + .left = left, + .right = right, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .flags = 0, + }; + + switch (operator->type) { + case YP_TOKEN_DOT_DOT_DOT: + case YP_TOKEN_UDOT_DOT_DOT: + node->flags |= YP_RANGE_NODE_FLAGS_EXCLUDE_END; + break; + default: + break; + } + + return node; +} + +// Allocate and initialize a new RedoNode node. +static yp_redo_node_t * +yp_redo_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_KEYWORD_REDO); + yp_redo_node_t *node = YP_ALLOC_NODE(parser, yp_redo_node_t); + + *node = (yp_redo_node_t) {{ .type = YP_NODE_REDO_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate a new RegularExpressionNode node. +static yp_regular_expression_node_t * +yp_regular_expression_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing) { + yp_regular_expression_node_t *node = YP_ALLOC_NODE(parser, yp_regular_expression_node_t); + + *node = (yp_regular_expression_node_t) { + { + .type = YP_NODE_REGULAR_EXPRESSION_NODE, + .location = { + .start = opening->start, + .end = closing->end + } + }, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .content_loc = YP_LOCATION_TOKEN_VALUE(content), + .closing_loc = YP_LOCATION_TOKEN_VALUE(closing), + .flags = yp_regular_expression_flags_create(closing) + }; + + return node; +} + +// Allocate a new RequiredDestructuredParameterNode node. +static yp_required_destructured_parameter_node_t * +yp_required_destructured_parameter_node_create(yp_parser_t *parser, const yp_token_t *opening) { + yp_required_destructured_parameter_node_t *node = YP_ALLOC_NODE(parser, yp_required_destructured_parameter_node_t); + + *node = (yp_required_destructured_parameter_node_t) { + { + .type = YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE, + .location = YP_LOCATION_TOKEN_VALUE(opening) + }, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .closing_loc = { .start = NULL, .end = NULL }, + .parameters = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Append a new parameter to the given RequiredDestructuredParameterNode node. +static void +yp_required_destructured_parameter_node_append_parameter(yp_required_destructured_parameter_node_t *node, yp_node_t *parameter) { + yp_node_list_append(&node->parameters, parameter); +} + +// Set the closing token of the given RequiredDestructuredParameterNode node. +static void +yp_required_destructured_parameter_node_closing_set(yp_required_destructured_parameter_node_t *node, const yp_token_t *closing) { + node->closing_loc = YP_LOCATION_TOKEN_VALUE(closing); + node->base.location.end = closing->end; +} + +// Allocate a new RequiredParameterNode node. +static yp_required_parameter_node_t * +yp_required_parameter_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_MISSING || token->type == YP_TOKEN_IDENTIFIER); + yp_required_parameter_node_t *node = YP_ALLOC_NODE(parser, yp_required_parameter_node_t); + + *node = (yp_required_parameter_node_t) { + { + .type = YP_NODE_REQUIRED_PARAMETER_NODE, + .location = YP_LOCATION_TOKEN_VALUE(token) + }, + .constant_id = yp_parser_constant_id_token(parser, token) + }; + + return node; +} + +// Allocate a new RescueModifierNode node. +static yp_rescue_modifier_node_t * +yp_rescue_modifier_node_create(yp_parser_t *parser, yp_node_t *expression, const yp_token_t *keyword, yp_node_t *rescue_expression) { + yp_rescue_modifier_node_t *node = YP_ALLOC_NODE(parser, yp_rescue_modifier_node_t); + + *node = (yp_rescue_modifier_node_t) { + { + .type = YP_NODE_RESCUE_MODIFIER_NODE, + .location = { + .start = expression->location.start, + .end = rescue_expression->location.end + } + }, + .expression = expression, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .rescue_expression = rescue_expression + }; + + return node; +} + +// Allocate and initiliaze a new RescueNode node. +static yp_rescue_node_t * +yp_rescue_node_create(yp_parser_t *parser, const yp_token_t *keyword) { + yp_rescue_node_t *node = YP_ALLOC_NODE(parser, yp_rescue_node_t); + + *node = (yp_rescue_node_t) { + { + .type = YP_NODE_RESCUE_NODE, + .location = { + .start = keyword->start, + .end = keyword->end + } + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .operator_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE, + .exception = NULL, + .statements = NULL, + .consequent = NULL, + .exceptions = YP_EMPTY_NODE_LIST + }; + + return node; +} + +static inline void +yp_rescue_node_operator_set(yp_rescue_node_t *node, const yp_token_t *operator) { + node->operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator); +} + +// Set the exception of a rescue node, and update the location of the node. +static void +yp_rescue_node_exception_set(yp_rescue_node_t *node, yp_node_t *exception) { + node->exception = exception; + node->base.location.end = exception->location.end; +} + +// Set the statements of a rescue node, and update the location of the node. +static void +yp_rescue_node_statements_set(yp_rescue_node_t *node, yp_statements_node_t *statements) { + node->statements = statements; + if ((statements != NULL) && (statements->body.size > 0)) { + node->base.location.end = statements->base.location.end; + } +} + +// Set the consequent of a rescue node, and update the location. +static void +yp_rescue_node_consequent_set(yp_rescue_node_t *node, yp_rescue_node_t *consequent) { + node->consequent = consequent; + node->base.location.end = consequent->base.location.end; +} + +// Append an exception node to a rescue node, and update the location. +static void +yp_rescue_node_exceptions_append(yp_rescue_node_t *node, yp_node_t *exception) { + yp_node_list_append(&node->exceptions, exception); + node->base.location.end = exception->location.end; +} + +// Allocate a new RestParameterNode node. +static yp_rest_parameter_node_t * +yp_rest_parameter_node_create(yp_parser_t *parser, const yp_token_t *operator, const yp_token_t *name) { + yp_rest_parameter_node_t *node = YP_ALLOC_NODE(parser, yp_rest_parameter_node_t); + + *node = (yp_rest_parameter_node_t) { + { + .type = YP_NODE_REST_PARAMETER_NODE, + .location = { + .start = operator->start, + .end = (name->type == YP_TOKEN_NOT_PROVIDED ? operator->end : name->end) + } + }, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .name_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(name) + }; + + return node; +} + +// Allocate and initialize a new RetryNode node. +static yp_retry_node_t * +yp_retry_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_KEYWORD_RETRY); + yp_retry_node_t *node = YP_ALLOC_NODE(parser, yp_retry_node_t); + + *node = (yp_retry_node_t) {{ .type = YP_NODE_RETRY_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate a new ReturnNode node. +static yp_return_node_t * +yp_return_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_arguments_node_t *arguments) { + yp_return_node_t *node = YP_ALLOC_NODE(parser, yp_return_node_t); + + *node = (yp_return_node_t) { + { + .type = YP_NODE_RETURN_NODE, + .location = { + .start = keyword->start, + .end = (arguments == NULL ? keyword->end : arguments->base.location.end) + } + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .arguments = arguments + }; + + return node; +} + +// Allocate and initialize a new SelfNode node. +static yp_self_node_t * +yp_self_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_KEYWORD_SELF); + yp_self_node_t *node = YP_ALLOC_NODE(parser, yp_self_node_t); + + *node = (yp_self_node_t) {{ .type = YP_NODE_SELF_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate a new SingletonClassNode node. +static yp_singleton_class_node_t * +yp_singleton_class_node_create(yp_parser_t *parser, yp_constant_id_list_t *locals, const yp_token_t *class_keyword, const yp_token_t *operator, yp_node_t *expression, yp_node_t *statements, const yp_token_t *end_keyword) { + yp_singleton_class_node_t *node = YP_ALLOC_NODE(parser, yp_singleton_class_node_t); + + *node = (yp_singleton_class_node_t) { + { + .type = YP_NODE_SINGLETON_CLASS_NODE, + .location = { + .start = class_keyword->start, + .end = end_keyword->end + } + }, + .locals = *locals, + .class_keyword_loc = YP_LOCATION_TOKEN_VALUE(class_keyword), + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .expression = expression, + .statements = statements, + .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword) + }; + + return node; +} + +// Allocate and initialize a new SourceEncodingNode node. +static yp_source_encoding_node_t * +yp_source_encoding_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_KEYWORD___ENCODING__); + yp_source_encoding_node_t *node = YP_ALLOC_NODE(parser, yp_source_encoding_node_t); + + *node = (yp_source_encoding_node_t) {{ .type = YP_NODE_SOURCE_ENCODING_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate and initialize a new SourceFileNode node. +static yp_source_file_node_t* +yp_source_file_node_create(yp_parser_t *parser, const yp_token_t *file_keyword) { + yp_source_file_node_t *node = YP_ALLOC_NODE(parser, yp_source_file_node_t); + assert(file_keyword->type == YP_TOKEN_KEYWORD___FILE__); + + *node = (yp_source_file_node_t) { + { + .type = YP_NODE_SOURCE_FILE_NODE, + .location = YP_LOCATION_TOKEN_VALUE(file_keyword), + }, + .filepath = parser->filepath_string, + }; + + return node; +} + +// Allocate and initialize a new SourceLineNode node. +static yp_source_line_node_t * +yp_source_line_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_KEYWORD___LINE__); + yp_source_line_node_t *node = YP_ALLOC_NODE(parser, yp_source_line_node_t); + + *node = (yp_source_line_node_t) {{ .type = YP_NODE_SOURCE_LINE_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate a new SplatNode node. +static yp_splat_node_t * +yp_splat_node_create(yp_parser_t *parser, const yp_token_t *operator, yp_node_t *expression) { + yp_splat_node_t *node = YP_ALLOC_NODE(parser, yp_splat_node_t); + + *node = (yp_splat_node_t) { + { + .type = YP_NODE_SPLAT_NODE, + .location = { + .start = operator->start, + .end = (expression == NULL ? operator->end : expression->location.end) + } + }, + .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), + .expression = expression + }; + + return node; +} + +// Allocate and initialize a new StatementsNode node. +static yp_statements_node_t * +yp_statements_node_create(yp_parser_t *parser) { + yp_statements_node_t *node = YP_ALLOC_NODE(parser, yp_statements_node_t); + + *node = (yp_statements_node_t) { + { + .type = YP_NODE_STATEMENTS_NODE, + .location = YP_LOCATION_NULL_VALUE(parser) + }, + .body = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Get the length of the given StatementsNode node's body. +static size_t +yp_statements_node_body_length(yp_statements_node_t *node) { + return node && node->body.size; +} + +// Set the location of the given StatementsNode. +static void +yp_statements_node_location_set(yp_statements_node_t *node, const char *start, const char *end) { + node->base.location = (yp_location_t) { .start = start, .end = end }; +} + +// Append a new node to the given StatementsNode node's body. +static void +yp_statements_node_body_append(yp_statements_node_t *node, yp_node_t *statement) { + if (yp_statements_node_body_length(node) == 0) { + node->base.location.start = statement->location.start; + } + + yp_node_list_append(&node->body, statement); + node->base.location.end = statement->location.end; +} + +// Allocate a new StringConcatNode node. +static yp_string_concat_node_t * +yp_string_concat_node_create(yp_parser_t *parser, yp_node_t *left, yp_node_t *right) { + yp_string_concat_node_t *node = YP_ALLOC_NODE(parser, yp_string_concat_node_t); + + *node = (yp_string_concat_node_t) { + { + .type = YP_NODE_STRING_CONCAT_NODE, + .location = { + .start = left->location.start, + .end = right->location.end + } + }, + .left = left, + .right = right + }; + + return node; +} + +// Allocate a new StringNode node. +static yp_string_node_t * +yp_string_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing) { + yp_string_node_t *node = YP_ALLOC_NODE(parser, yp_string_node_t); + + *node = (yp_string_node_t) { + { + .type = YP_NODE_STRING_NODE, + .location = { + .start = (opening->type == YP_TOKEN_NOT_PROVIDED ? content->start : opening->start), + .end = (closing->type == YP_TOKEN_NOT_PROVIDED ? content->end : closing->end) + } + }, + .opening_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .content_loc = YP_LOCATION_TOKEN_VALUE(content), + .closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .unescaped = YP_EMPTY_STRING + }; + + return node; +} + +// Allocate and initialize a new SuperNode node. +static yp_super_node_t * +yp_super_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_arguments_t *arguments) { + assert(keyword->type == YP_TOKEN_KEYWORD_SUPER); + yp_super_node_t *node = YP_ALLOC_NODE(parser, yp_super_node_t); + + const char *end; + if (arguments->block != NULL) { + end = arguments->block->base.location.end; + } else if (arguments->closing_loc.start != NULL) { + end = arguments->closing_loc.end; + } else if (arguments->arguments != NULL) { + end = arguments->arguments->base.location.end; + } else { + assert(false && "unreachable"); + end = NULL; + } + + *node = (yp_super_node_t) { + { + .type = YP_NODE_SUPER_NODE, + .location = { + .start = keyword->start, + .end = end, + } + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .lparen_loc = arguments->opening_loc, + .arguments = arguments->arguments, + .rparen_loc = arguments->closing_loc, + .block = arguments->block + }; + + return node; +} + +// Allocate a new SymbolNode node. +static yp_symbol_node_t * +yp_symbol_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *value, const yp_token_t *closing) { + yp_symbol_node_t *node = YP_ALLOC_NODE(parser, yp_symbol_node_t); + + *node = (yp_symbol_node_t) { + { + .type = YP_NODE_SYMBOL_NODE, + .location = { + .start = (opening->type == YP_TOKEN_NOT_PROVIDED ? value->start : opening->start), + .end = (closing->type == YP_TOKEN_NOT_PROVIDED ? value->end : closing->end) + } + }, + .opening_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(opening), + .value_loc = YP_LOCATION_TOKEN_VALUE(value), + .closing_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(closing), + .unescaped = YP_EMPTY_STRING + }; + + return node; +} + +// Allocate and initialize a new SymbolNode node from a label. +static yp_symbol_node_t * +yp_symbol_node_label_create(yp_parser_t *parser, const yp_token_t *token) { + yp_symbol_node_t *node; + + switch (token->type) { + case YP_TOKEN_LABEL: { + yp_token_t opening = not_provided(parser); + yp_token_t closing = { .type = YP_TOKEN_LABEL_END, .start = token->end - 1, .end = token->end }; + + yp_token_t label = { .type = YP_TOKEN_LABEL, .start = token->start, .end = token->end - 1 }; + node = yp_symbol_node_create(parser, &opening, &label, &closing); + + ptrdiff_t length = label.end - label.start; + assert(length >= 0); + + yp_unescape_manipulate_string(parser, label.start, (size_t) length, &node->unescaped, YP_UNESCAPE_ALL, &parser->error_list); + break; + } + case YP_TOKEN_MISSING: { + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + + yp_token_t label = { .type = YP_TOKEN_LABEL, .start = token->start, .end = token->end }; + node = yp_symbol_node_create(parser, &opening, &label, &closing); + break; + } + default: + assert(false && "unreachable"); + node = NULL; + break; + } + + return node; +} + +// Check if the given node is a label in a hash. +static bool +yp_symbol_node_label_p(yp_node_t *node) { + const char *end = NULL; + + switch (node->type) { + case YP_NODE_SYMBOL_NODE: + end = ((yp_symbol_node_t *) node)->closing_loc.end; + break; + case YP_NODE_INTERPOLATED_SYMBOL_NODE: + end = ((yp_interpolated_symbol_node_t *) node)->closing_loc.end; + break; + default: + return false; + } + + return (end != NULL) && (end[-1] == ':'); +} + +// Convert the given SymbolNode node to a StringNode node. +static yp_string_node_t * +yp_symbol_node_to_string_node(yp_parser_t *parser, yp_symbol_node_t *node) { + yp_string_node_t *new_node = YP_ALLOC_NODE(parser, yp_string_node_t); + + *new_node = (yp_string_node_t) { + { + .type = YP_NODE_STRING_NODE, + .location = node->base.location + }, + .opening_loc = node->opening_loc, + .content_loc = node->value_loc, + .closing_loc = node->closing_loc, + .unescaped = node->unescaped + }; + + // We are explicitly _not_ using yp_node_destroy here because we don't want + // to trash the unescaped string. We could instead copy the string if we + // know that it is owned, but we're taking the fast path for now. + free(node); + + return new_node; +} + +// Allocate and initialize a new TrueNode node. +static yp_true_node_t * +yp_true_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_KEYWORD_TRUE); + yp_true_node_t *node = YP_ALLOC_NODE(parser, yp_true_node_t); + + *node = (yp_true_node_t) {{ .type = YP_NODE_TRUE_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; + return node; +} + +// Allocate and initialize a new UndefNode node. +static yp_undef_node_t * +yp_undef_node_create(yp_parser_t *parser, const yp_token_t *token) { + assert(token->type == YP_TOKEN_KEYWORD_UNDEF); + yp_undef_node_t *node = YP_ALLOC_NODE(parser, yp_undef_node_t); + + *node = (yp_undef_node_t) { + { + .type = YP_NODE_UNDEF_NODE, + .location = YP_LOCATION_TOKEN_VALUE(token), + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(token), + .names = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Append a name to an undef node. +static void +yp_undef_node_append(yp_undef_node_t *node, yp_node_t *name) { + node->base.location.end = name->location.end; + yp_node_list_append(&node->names, name); +} + +// Allocate a new UnlessNode node. +static yp_unless_node_t * +yp_unless_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements) { + yp_unless_node_t *node = YP_ALLOC_NODE(parser, yp_unless_node_t); + + const char *end; + if (statements != NULL) { + end = statements->base.location.end; + } else { + end = predicate->location.end; + } + + *node = (yp_unless_node_t) { + { + .type = YP_NODE_UNLESS_NODE, + .location = { + .start = keyword->start, + .end = end + }, + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .predicate = predicate, + .statements = statements, + .consequent = NULL, + .end_keyword_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +// Allocate and initialize new UnlessNode node in the modifier form. +static yp_unless_node_t * +yp_unless_node_modifier_create(yp_parser_t *parser, yp_node_t *statement, const yp_token_t *unless_keyword, yp_node_t *predicate) { + yp_unless_node_t *node = YP_ALLOC_NODE(parser, yp_unless_node_t); + + yp_statements_node_t *statements = yp_statements_node_create(parser); + yp_statements_node_body_append(statements, statement); + + *node = (yp_unless_node_t) { + { + .type = YP_NODE_UNLESS_NODE, + .location = { + .start = statement->location.start, + .end = predicate->location.end + }, + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(unless_keyword), + .predicate = predicate, + .statements = statements, + .consequent = NULL, + .end_keyword_loc = YP_OPTIONAL_LOCATION_NOT_PROVIDED_VALUE + }; + + return node; +} + +static inline void +yp_unless_node_end_keyword_loc_set(yp_unless_node_t *node, const yp_token_t *end_keyword) { + node->end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword); + node->base.location.end = end_keyword->end; +} + +// Allocate a new UntilNode node. +static yp_until_node_t * +yp_until_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements) { + yp_until_node_t *node = YP_ALLOC_NODE(parser, yp_until_node_t); + bool has_statements = (statements != NULL) && (statements->body.size != 0); + + const char *start = NULL; + if (has_statements && (keyword->start > statements->base.location.start)) { + start = statements->base.location.start; + } else { + start = keyword->start; + } + + const char *end = NULL; + if (has_statements && (predicate->location.end < statements->base.location.end)) { + end = statements->base.location.end; + } else { + end = predicate->location.end; + } + + *node = (yp_until_node_t) { + { + .type = YP_NODE_UNTIL_NODE, + .location = { + .start = start, + .end = end, + }, + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .predicate = predicate, + .statements = statements + }; + + return node; +} + +// Allocate and initialize a new WhenNode node. +static yp_when_node_t * +yp_when_node_create(yp_parser_t *parser, const yp_token_t *keyword) { + yp_when_node_t *node = YP_ALLOC_NODE(parser, yp_when_node_t); + + *node = (yp_when_node_t) { + { + .type = YP_NODE_WHEN_NODE, + .location = { + .start = keyword->start, + .end = NULL + } + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .statements = NULL, + .conditions = YP_EMPTY_NODE_LIST + }; + + return node; +} + +// Append a new condition to a when node. +static void +yp_when_node_conditions_append(yp_when_node_t *node, yp_node_t *condition) { + node->base.location.end = condition->location.end; + yp_node_list_append(&node->conditions, condition); +} + +// Set the statements list of a when node. +static void +yp_when_node_statements_set(yp_when_node_t *node, yp_statements_node_t *statements) { + if (statements->base.location.end > node->base.location.end) { + node->base.location.end = statements->base.location.end; + } + + node->statements = statements; +} + +// Allocate a new WhileNode node. +static yp_while_node_t * +yp_while_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements) { + yp_while_node_t *node = YP_ALLOC_NODE(parser, yp_while_node_t); + + const char *start = NULL; + bool has_statements = (statements != NULL) && (statements->body.size != 0); + if (has_statements && (keyword->start > statements->base.location.start)) { + start = statements->base.location.start; + } else { + start = keyword->start; + } + + const char *end = NULL; + if (has_statements && (predicate->location.end < statements->base.location.end)) { + end = statements->base.location.end; + } else { + end = predicate->location.end; + } + + *node = (yp_while_node_t) { + { + .type = YP_NODE_WHILE_NODE, + .location = { + .start = start, + .end = end, + }, + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .predicate = predicate, + .statements = statements + }; + + return node; +} + +// Allocate and initialize a new XStringNode node. +static yp_x_string_node_t * +yp_xstring_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing) { + yp_x_string_node_t *node = YP_ALLOC_NODE(parser, yp_x_string_node_t); + + *node = (yp_x_string_node_t) { + { + .type = YP_NODE_X_STRING_NODE, + .location = { + .start = opening->start, + .end = closing->end + }, + }, + .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), + .content_loc = YP_LOCATION_TOKEN_VALUE(content), + .closing_loc = YP_LOCATION_TOKEN_VALUE(closing), + .unescaped = YP_EMPTY_STRING + }; + + return node; +} + +// Allocate a new YieldNode node. +static yp_yield_node_t * +yp_yield_node_create(yp_parser_t *parser, const yp_token_t *keyword, const yp_location_t *lparen_loc, yp_arguments_node_t *arguments, const yp_location_t *rparen_loc) { + yp_yield_node_t *node = YP_ALLOC_NODE(parser, yp_yield_node_t); + + const char *end; + if (rparen_loc->start != NULL) { + end = rparen_loc->end; + } else if (arguments != NULL) { + end = arguments->base.location.end; + } else if (lparen_loc->start != NULL) { + end = lparen_loc->end; + } else { + end = keyword->end; + } + + *node = (yp_yield_node_t) { + { + .type = YP_NODE_YIELD_NODE, + .location = { + .start = keyword->start, + .end = end + }, + }, + .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), + .lparen_loc = *lparen_loc, + .arguments = arguments, + .rparen_loc = *rparen_loc + }; + + return node; +} + + +#undef YP_EMPTY_STRING +#undef YP_LOCATION_NULL_VALUE +#undef YP_LOCATION_TOKEN_VALUE +#undef YP_LOCATION_NODE_VALUE +#undef YP_LOCATION_NODE_BASE_VALUE +#undef YP_TOKEN_NOT_PROVIDED_VALUE +#undef YP_ALLOC_NODE + +/******************************************************************************/ +/* Scope-related functions */ +/******************************************************************************/ + +// Allocate and initialize a new scope. Push it onto the scope stack. +static bool +yp_parser_scope_push(yp_parser_t *parser, bool closed) { + yp_scope_t *scope = (yp_scope_t *) malloc(sizeof(yp_scope_t)); + if (scope == NULL) return false; + + *scope = (yp_scope_t) { .closed = closed, .previous = parser->current_scope }; + yp_constant_id_list_init(&scope->locals); + + parser->current_scope = scope; + return true; +} + +// Check if the current scope has a given local variables. +static int +yp_parser_local_depth(yp_parser_t *parser, yp_token_t *token) { + yp_constant_id_t constant_id = yp_parser_constant_id_token(parser, token); + yp_scope_t *scope = parser->current_scope; + int depth = 0; + + while (scope != NULL) { + if (yp_constant_id_list_includes(&scope->locals, constant_id)) return depth; + if (scope->closed) break; + + scope = scope->previous; + depth++; + } + + return -1; +} + +// Add a local variable from a location to the current scope. +static void +yp_parser_local_add_location(yp_parser_t *parser, const char *start, const char *end) { + yp_constant_id_t constant_id = yp_parser_constant_id_location(parser, start, end); + + if (!yp_constant_id_list_includes(&parser->current_scope->locals, constant_id)) { + yp_constant_id_list_append(&parser->current_scope->locals, constant_id); + } +} + +// Add a local variable from a token to the current scope. +static inline void +yp_parser_local_add_token(yp_parser_t *parser, yp_token_t *token) { + yp_parser_local_add_location(parser, token->start, token->end); +} + +// Add a parameter name to the current scope and check whether the name of the +// parameter is unique or not. +static void +yp_parser_parameter_name_check(yp_parser_t *parser, yp_token_t *name) { + // We want to ignore any parameter name that starts with an underscore. + if ((*name->start == '_')) return; + + // Otherwise we'll fetch the constant id for the parameter name and check + // whether it's already in the current scope. + yp_constant_id_t constant_id = yp_parser_constant_id_token(parser, name); + + if (yp_constant_id_list_includes(&parser->current_scope->locals, constant_id)) { + yp_diagnostic_list_append(&parser->error_list, name->start, name->end, "Duplicated parameter name."); + } +} + +// Pop the current scope off the scope stack. +static void +yp_parser_scope_pop(yp_parser_t *parser) { + yp_scope_t *scope = parser->current_scope; + parser->current_scope = scope->previous; + free(scope); +} + +/******************************************************************************/ +/* Basic character checks */ +/******************************************************************************/ + +// This function is used extremely frequently to lex all of the identifiers in a +// source file, so it's important that it be as fast as possible. For this +// reason we have the encoding_changed boolean to check if we need to go through +// the function pointer or can just directly use the UTF-8 functions. +static inline size_t +char_is_identifier_start(yp_parser_t *parser, const char *c) { + const unsigned char uc = (unsigned char) *c; + + return ( + (parser->encoding_changed + ? parser->encoding.alpha_char(c) + : (uc < 0x80 ? (yp_encoding_unicode_table[uc] & YP_ENCODING_ALPHABETIC_BIT ? 1 : 0) : yp_encoding_utf_8_alpha_char(c)) + ) || (uc == '_') || (uc >= 0x80) + ); +} + +// Like the above, this function is also used extremely frequently to lex all of +// the identifiers in a source file once the first character has been found. So +// it's important that it be as fast as possible. +static inline size_t +char_is_identifier(yp_parser_t *parser, const char *c) { + const unsigned char uc = (unsigned char) *c; + + return ( + (parser->encoding_changed + ? parser->encoding.alnum_char(c) + : (uc < 0x80 ? (yp_encoding_unicode_table[uc] & YP_ENCODING_ALPHANUMERIC_BIT ? 1 : 0) : yp_encoding_utf_8_alnum_char(c)) + ) || (uc == '_') || (uc >= 0x80) + ); +} + +// Here we're defining a perfect hash for the characters that are allowed in +// global names. This is used to quickly check the next character after a $ to +// see if it's a valid character for a global name. +#define BIT(c, idx) (((c) / 32 - 1 == idx) ? (1U << ((c) % 32)) : 0) +#define PUNCT(idx) ( \ + BIT('~', idx) | BIT('*', idx) | BIT('$', idx) | BIT('?', idx) | \ + BIT('!', idx) | BIT('@', idx) | BIT('/', idx) | BIT('\\', idx) | \ + BIT(';', idx) | BIT(',', idx) | BIT('.', idx) | BIT('=', idx) | \ + BIT(':', idx) | BIT('<', idx) | BIT('>', idx) | BIT('\"', idx) | \ + BIT('&', idx) | BIT('`', idx) | BIT('\'', idx) | BIT('+', idx) | \ + BIT('0', idx)) + +const unsigned int yp_global_name_punctuation_hash[(0x7e - 0x20 + 31) / 32] = { PUNCT(0), PUNCT(1), PUNCT(2) }; + +#undef BIT +#undef PUNCT + +static inline bool +char_is_global_name_punctuation(const char c) { + const unsigned int i = (const unsigned int) c; + if (i <= 0x20 || 0x7e < i) return false; + + return (yp_global_name_punctuation_hash[(i - 0x20) / 32] >> (c % 32)) & 1; +} + +static inline bool +token_is_numbered_parameter(const char *start, const char *end) { + return (end - start == 2) && (start[0] == '_') && (start[1] != '0') && (yp_char_is_decimal_digit(start[1])); +} + +static inline bool +token_is_setter_name(yp_token_t *token) { + return ( + (token->type == YP_TOKEN_IDENTIFIER) && + (token->end - token->start >= 2) && + (token->end[-1] == '=') + ); +} + +/******************************************************************************/ +/* Stack helpers */ +/******************************************************************************/ + +static inline void +yp_accepts_block_stack_push(yp_parser_t *parser, bool value) { + // Use the negation of the value to prevent stack overflow. + yp_state_stack_push(&parser->accepts_block_stack, !value); +} + +static inline void +yp_accepts_block_stack_pop(yp_parser_t *parser) { + yp_state_stack_pop(&parser->accepts_block_stack); +} + +static inline bool +yp_accepts_block_stack_p(yp_parser_t *parser) { + return !yp_state_stack_p(&parser->accepts_block_stack); +} + +static inline void +yp_do_loop_stack_push(yp_parser_t *parser, bool value) { + yp_state_stack_push(&parser->do_loop_stack, value); +} + +static inline void +yp_do_loop_stack_pop(yp_parser_t *parser) { + yp_state_stack_pop(&parser->do_loop_stack); +} + +static inline bool +yp_do_loop_stack_p(yp_parser_t *parser) { + return yp_state_stack_p(&parser->do_loop_stack); +} + +/******************************************************************************/ +/* Lexer check helpers */ +/******************************************************************************/ + +// Get the next character in the source starting from parser->current.end and +// adding the given offset. If that position is beyond the end of the source +// then return '\0'. +static inline char +peek_at(yp_parser_t *parser, size_t offset) { + if (parser->current.end + offset < parser->end) { + return parser->current.end[offset]; + } else { + return '\0'; + } +} + +// Get the next character in the source starting from parser->current.end. If +// that position is beyond the end of the source then return '\0'. +static inline char +peek(yp_parser_t *parser) { + if (parser->current.end < parser->end) { + return *parser->current.end; + } else { + return '\0'; + } +} + +// If the character to be read matches the given value, then returns true and +// advanced the current pointer. +static inline bool +match(yp_parser_t *parser, char value) { + if (peek(parser) == value) { + parser->current.end++; + return true; + } + return false; +} + +// Skip to the next newline character or NUL byte. +static inline const char * +next_newline(const char *cursor, ptrdiff_t length) { + assert(length >= 0); + + // Note that it's okay for us to use memchr here to look for \n because none + // of the encodings that we support have \n as a component of a multi-byte + // character. + return memchr(cursor, '\n', (size_t) length); +} + +// Find the start of the encoding comment. This is effectively an inlined +// version of strnstr with some modifications. +static inline const char * +parser_lex_encoding_comment_start(yp_parser_t *parser, const char *cursor, ptrdiff_t remaining) { + assert(remaining >= 0); + size_t length = (size_t) remaining; + + size_t key_length = strlen("coding:"); + if (key_length > length) return NULL; + + const char *cursor_limit = cursor + length - key_length + 1; + while ((cursor = yp_memchr(parser, cursor, 'c', (size_t) (cursor_limit - cursor))) != NULL) { + if ( + (strncmp(cursor, "coding", key_length - 1) == 0) && + (cursor[key_length - 1] == ':' || cursor[key_length - 1] == '=') + ) { + return cursor + key_length; + } + + cursor++; + } + + return NULL; +} + +// Here we're going to check if this is a "magic" comment, and perform whatever +// actions are necessary for it here. +static void +parser_lex_encoding_comment(yp_parser_t *parser) { + const char *start = parser->current.start + 1; + const char *end = next_newline(start, parser->end - start); + if (end == NULL) end = parser->end; + + // These are the patterns we're going to match to find the encoding comment. + // This is definitely not complete or even really correct. + const char *encoding_start = parser_lex_encoding_comment_start(parser, start, end - start); + + // If we didn't find anything that matched our patterns, then return. Note + // that this does a _very_ poor job of actually finding the encoding, and + // there is a lot of work to do here to better reflect actual magic comment + // parsing from CRuby, but this at least gets us part of the way there. + if (encoding_start == NULL) return; + + // Skip any non-newline whitespace after the "coding:" or "coding=". + encoding_start += yp_strspn_inline_whitespace(encoding_start, end - encoding_start); + + // Now determine the end of the encoding string. This is either the end of + // the line, the first whitespace character, or a punctuation mark. + const char *encoding_end = yp_strpbrk(parser, encoding_start, " \t\f\r\v\n;,", end - encoding_start); + encoding_end = encoding_end == NULL ? end : encoding_end; + + // Finally, we can determine the width of the encoding string. + size_t width = (size_t) (encoding_end - encoding_start); + + // First, we're going to call out to a user-defined callback if one was + // provided. If they return an encoding struct that we can use, then we'll + // use that here. + if (parser->encoding_decode_callback != NULL) { + yp_encoding_t *encoding = parser->encoding_decode_callback(parser, encoding_start, width); + + if (encoding != NULL) { + parser->encoding = *encoding; + return; + } + } + + // Next, we're going to check for UTF-8. This is the most common encoding. + // Extensions like utf-8 can contain extra encoding details like, + // utf-8-dos, utf-8-linux, utf-8-mac. We treat these all as utf-8 should + // treat any encoding starting utf-8 as utf-8. + if (strncasecmp(encoding_start, "utf-8", 5) == 0) { + // We don't need to do anything here because the default encoding is + // already UTF-8. We'll just return. + return; + } + + // Next, we're going to loop through each of the encodings that we handle + // explicitly. If we found one that we understand, we'll use that value. +#define ENCODING(value, prebuilt) \ + if (width == sizeof(value) - 1 && strncasecmp(encoding_start, value, sizeof(value) - 1) == 0) { \ + parser->encoding = prebuilt; \ + parser->encoding_changed |= true; \ + if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser); \ + return; \ + } + + // Check most common first. (This is pretty arbitrary.) + ENCODING("ascii", yp_encoding_ascii); + ENCODING("ascii-8bit", yp_encoding_ascii_8bit); + ENCODING("us-ascii", yp_encoding_ascii); + ENCODING("binary", yp_encoding_ascii_8bit); + ENCODING("shift_jis", yp_encoding_shift_jis); + ENCODING("euc-jp", yp_encoding_euc_jp); + + // Then check all the others. + ENCODING("big5", yp_encoding_big5); + ENCODING("gbk", yp_encoding_gbk); + ENCODING("iso-8859-1", yp_encoding_iso_8859_1); + ENCODING("iso-8859-2", yp_encoding_iso_8859_2); + ENCODING("iso-8859-3", yp_encoding_iso_8859_3); + ENCODING("iso-8859-4", yp_encoding_iso_8859_4); + ENCODING("iso-8859-5", yp_encoding_iso_8859_5); + ENCODING("iso-8859-6", yp_encoding_iso_8859_6); + ENCODING("iso-8859-7", yp_encoding_iso_8859_7); + ENCODING("iso-8859-8", yp_encoding_iso_8859_8); + ENCODING("iso-8859-9", yp_encoding_iso_8859_9); + ENCODING("iso-8859-10", yp_encoding_iso_8859_10); + ENCODING("iso-8859-11", yp_encoding_iso_8859_11); + ENCODING("iso-8859-13", yp_encoding_iso_8859_13); + ENCODING("iso-8859-14", yp_encoding_iso_8859_14); + ENCODING("iso-8859-15", yp_encoding_iso_8859_15); + ENCODING("iso-8859-16", yp_encoding_iso_8859_16); + ENCODING("koi8-r", yp_encoding_koi8_r); + ENCODING("windows-31j", yp_encoding_windows_31j); + ENCODING("windows-1251", yp_encoding_windows_1251); + ENCODING("windows-1252", yp_encoding_windows_1252); + ENCODING("cp1251", yp_encoding_windows_1251); + ENCODING("cp1252", yp_encoding_windows_1252); + ENCODING("cp932", yp_encoding_windows_31j); + ENCODING("sjis", yp_encoding_windows_31j); + +#undef ENCODING + + // If nothing was returned by this point, then we've got an issue because we + // didn't understand the encoding that the user was trying to use. In this + // case we'll keep using the default encoding but add an error to the + // parser to indicate an unsuccessful parse. + yp_diagnostic_list_append(&parser->error_list, encoding_start, encoding_end, "Could not understand the encoding specified in the magic comment."); +} + +/******************************************************************************/ +/* Context manipulations */ +/******************************************************************************/ + +static bool +context_terminator(yp_context_t context, yp_token_t *token) { + switch (context) { + case YP_CONTEXT_MAIN: + case YP_CONTEXT_DEF_PARAMS: + return token->type == YP_TOKEN_EOF; + case YP_CONTEXT_DEFAULT_PARAMS: + return token->type == YP_TOKEN_COMMA || token->type == YP_TOKEN_PARENTHESIS_RIGHT; + case YP_CONTEXT_PREEXE: + case YP_CONTEXT_POSTEXE: + return token->type == YP_TOKEN_BRACE_RIGHT; + case YP_CONTEXT_MODULE: + case YP_CONTEXT_CLASS: + case YP_CONTEXT_SCLASS: + case YP_CONTEXT_LAMBDA_DO_END: + case YP_CONTEXT_DEF: + case YP_CONTEXT_BLOCK_KEYWORDS: + return token->type == YP_TOKEN_KEYWORD_END || token->type == YP_TOKEN_KEYWORD_RESCUE || token->type == YP_TOKEN_KEYWORD_ENSURE; + case YP_CONTEXT_WHILE: + case YP_CONTEXT_UNTIL: + case YP_CONTEXT_ELSE: + case YP_CONTEXT_FOR: + case YP_CONTEXT_ENSURE: + return token->type == YP_TOKEN_KEYWORD_END; + case YP_CONTEXT_CASE_WHEN: + return token->type == YP_TOKEN_KEYWORD_WHEN || token->type == YP_TOKEN_KEYWORD_END || token->type == YP_TOKEN_KEYWORD_ELSE; + case YP_CONTEXT_CASE_IN: + return token->type == YP_TOKEN_KEYWORD_IN || token->type == YP_TOKEN_KEYWORD_END || token->type == YP_TOKEN_KEYWORD_ELSE; + case YP_CONTEXT_IF: + case YP_CONTEXT_ELSIF: + return token->type == YP_TOKEN_KEYWORD_ELSE || token->type == YP_TOKEN_KEYWORD_ELSIF || token->type == YP_TOKEN_KEYWORD_END; + case YP_CONTEXT_UNLESS: + return token->type == YP_TOKEN_KEYWORD_ELSE || token->type == YP_TOKEN_KEYWORD_END; + case YP_CONTEXT_EMBEXPR: + return token->type == YP_TOKEN_EMBEXPR_END; + case YP_CONTEXT_BLOCK_BRACES: + return token->type == YP_TOKEN_BRACE_RIGHT; + case YP_CONTEXT_PARENS: + return token->type == YP_TOKEN_PARENTHESIS_RIGHT; + case YP_CONTEXT_BEGIN: + case YP_CONTEXT_RESCUE: + return token->type == YP_TOKEN_KEYWORD_ENSURE || token->type == YP_TOKEN_KEYWORD_RESCUE || token->type == YP_TOKEN_KEYWORD_ELSE || token->type == YP_TOKEN_KEYWORD_END; + case YP_CONTEXT_RESCUE_ELSE: + return token->type == YP_TOKEN_KEYWORD_ENSURE || token->type == YP_TOKEN_KEYWORD_END; + case YP_CONTEXT_LAMBDA_BRACES: + return token->type == YP_TOKEN_BRACE_RIGHT; + case YP_CONTEXT_PREDICATE: + return token->type == YP_TOKEN_KEYWORD_THEN || token->type == YP_TOKEN_NEWLINE || token->type == YP_TOKEN_SEMICOLON; + } + + return false; +} + +static bool +context_recoverable(yp_parser_t *parser, yp_token_t *token) { + yp_context_node_t *context_node = parser->current_context; + + while (context_node != NULL) { + if (context_terminator(context_node->context, token)) return true; + context_node = context_node->prev; + } + + return false; +} + +static bool +context_push(yp_parser_t *parser, yp_context_t context) { + yp_context_node_t *context_node = (yp_context_node_t *) malloc(sizeof(yp_context_node_t)); + if (context_node == NULL) return false; + + *context_node = (yp_context_node_t) { .context = context, .prev = NULL }; + + if (parser->current_context == NULL) { + parser->current_context = context_node; + } else { + context_node->prev = parser->current_context; + parser->current_context = context_node; + } + + return true; +} + +static void +context_pop(yp_parser_t *parser) { + if (parser->current_context->prev == NULL) { + free(parser->current_context); + parser->current_context = NULL; + } else { + yp_context_node_t *prev = parser->current_context->prev; + free(parser->current_context); + parser->current_context = prev; + } +} + +static bool +context_p(yp_parser_t *parser, yp_context_t context) { + yp_context_node_t *context_node = parser->current_context; + + while (context_node != NULL) { + if (context_node->context == context) return true; + context_node = context_node->prev; + } + + return false; +} + +static bool +context_def_p(yp_parser_t *parser) { + yp_context_node_t *context_node = parser->current_context; + + while (context_node != NULL) { + switch (context_node->context) { + case YP_CONTEXT_DEF: + return true; + case YP_CONTEXT_CLASS: + case YP_CONTEXT_MODULE: + case YP_CONTEXT_SCLASS: + return false; + default: + context_node = context_node->prev; + } + } + + return false; +} + +/******************************************************************************/ +/* Specific token lexers */ +/******************************************************************************/ + +static yp_token_type_t +lex_optional_float_suffix(yp_parser_t *parser) { + yp_token_type_t type = YP_TOKEN_INTEGER; + + // Here we're going to attempt to parse the optional decimal portion of a + // float. If it's not there, then it's okay and we'll just continue on. + if (peek(parser) == '.') { + if (yp_char_is_decimal_digit(peek_at(parser, 1))) { + parser->current.end += 2; + parser->current.end += yp_strspn_decimal_number(parser->current.end, parser->end - parser->current.end); + type = YP_TOKEN_FLOAT; + } else { + // If we had a . and then something else, then it's not a float suffix on + // a number it's a method call or something else. + return type; + } + } + + // Here we're going to attempt to parse the optional exponent portion of a + // float. If it's not there, it's okay and we'll just continue on. + if (match(parser, 'e') || match(parser, 'E')) { + (void) (match(parser, '+') || match(parser, '-')); + + if (yp_char_is_decimal_digit(*parser->current.end)) { + parser->current.end++; + parser->current.end += yp_strspn_decimal_number(parser->current.end, parser->end - parser->current.end); + type = YP_TOKEN_FLOAT; + } else { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Missing exponent."); + type = YP_TOKEN_FLOAT; + } + } + + return type; +} + +static yp_token_type_t +lex_numeric_prefix(yp_parser_t *parser) { + yp_token_type_t type = YP_TOKEN_INTEGER; + + if (parser->current.end[-1] == '0') { + switch (*parser->current.end) { + // 0d1111 is a decimal number + case 'd': + case 'D': + if (yp_char_is_decimal_digit(*++parser->current.end)) { + parser->current.end += yp_strspn_decimal_number(parser->current.end, parser->end - parser->current.end); + } else { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid decimal number."); + } + + break; + + // 0b1111 is a binary number + case 'b': + case 'B': + if (yp_char_is_binary_digit(*++parser->current.end)) { + parser->current.end += yp_strspn_binary_number(parser->current.end, parser->end - parser->current.end); + } else { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid binary number."); + } + + break; + + // 0o1111 is an octal number + case 'o': + case 'O': + if (yp_char_is_octal_digit(*++parser->current.end)) { + parser->current.end += yp_strspn_octal_number(parser->current.end, parser->end - parser->current.end); + } else { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid octal number."); + } + + break; + + // 01111 is an octal number + case '_': + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + parser->current.end += yp_strspn_octal_number(parser->current.end, parser->end - parser->current.end); + break; + + // 0x1111 is a hexadecimal number + case 'x': + case 'X': + if (yp_char_is_hexadecimal_digit(*++parser->current.end)) { + parser->current.end += yp_strspn_hexadecimal_number(parser->current.end, parser->end - parser->current.end); + } else { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid hexadecimal number."); + } + + break; + + // 0.xxx is a float + case '.': { + type = lex_optional_float_suffix(parser); + break; + } + + // 0exxx is a float + case 'e': + case 'E': { + type = lex_optional_float_suffix(parser); + break; + } + } + } else { + // If it didn't start with a 0, then we'll lex as far as we can into a + // decimal number. + parser->current.end += yp_strspn_decimal_number(parser->current.end, parser->end - parser->current.end); + + // Afterward, we'll lex as far as we can into an optional float suffix. + type = lex_optional_float_suffix(parser); + } + + // If the last character that we consumed was an underscore, then this is + // actually an invalid integer value, and we should return an invalid token. + if (parser->current.end[-1] == '_') { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Number literal cannot end with a `_`."); + } + + return type; +} + +static yp_token_type_t +lex_finalize_numeric_type(yp_parser_t *parser, yp_token_type_t numeric_type, const char *numeric_end, const char *rational_end, const char *imaginary_end) { + if (rational_end || imaginary_end) { + lex_mode_push(parser, (yp_lex_mode_t) { + .mode = YP_LEX_NUMERIC, + .as.numeric.type = numeric_type, + .as.numeric.start = parser->current.start, + .as.numeric.end = numeric_end + }); + } + + if (rational_end && imaginary_end) { + lex_mode_push(parser, (yp_lex_mode_t) { + .mode = YP_LEX_NUMERIC, + .as.numeric.type = YP_TOKEN_RATIONAL_NUMBER, + .as.numeric.start = parser->current.start, + .as.numeric.end = rational_end + }); + } + + if (imaginary_end) { + return YP_TOKEN_IMAGINARY_NUMBER; + } + + if (rational_end) { + return YP_TOKEN_RATIONAL_NUMBER; + } + + return numeric_type; +} + +static yp_token_type_t +lex_numeric(yp_parser_t *parser) { + yp_token_type_t type = YP_TOKEN_INTEGER; + + if (parser->current.end < parser->end) { + type = lex_numeric_prefix(parser); + + const char *end = parser->current.end; + const char *rational_end = NULL; + const char *imaginary_end = NULL; + + if (match(parser, 'r')) { + rational_end = parser->current.end; + } + + if (match(parser, 'i')) { + imaginary_end = parser->current.end; + } + + const unsigned char uc = (const unsigned char) peek(parser); + if (uc != '\0' && (uc >= 0x80 || ((uc >= 'a' && uc <= 'z') || (uc >= 'A' && uc <= 'Z')) || uc == '_')) { + parser->current.end = end; + } else { + type = lex_finalize_numeric_type(parser, type, end, rational_end, imaginary_end); + } + } + + return type; +} + +static yp_token_type_t +lex_global_variable(yp_parser_t *parser) { + switch (*parser->current.end) { + case '~': // $~: match-data + case '*': // $*: argv + case '$': // $$: pid + case '?': // $?: last status + case '!': // $!: error string + case '@': // $@: error position + case '/': // $/: input record separator + case '\\': // $\: output record separator + case ';': // $;: field separator + case ',': // $,: output field separator + case '.': // $.: last read line number + case '=': // $=: ignorecase + case ':': // $:: load path + case '<': // $<: reading filename + case '>': // $>: default output handle + case '\"': // $": already loaded files + parser->current.end++; + return YP_TOKEN_GLOBAL_VARIABLE; + + case '&': // $&: last match + case '`': // $`: string before last match + case '\'': // $': string after last match + case '+': // $+: string matches last paren. + parser->current.end++; + return lex_state_p(parser, YP_LEX_STATE_FNAME) ? YP_TOKEN_GLOBAL_VARIABLE : YP_TOKEN_BACK_REFERENCE; + + case '0': { + parser->current.end++; + size_t width; + + if ((width = char_is_identifier(parser, parser->current.end)) > 0) { + do { + parser->current.end += width; + } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0); + + // $0 isn't allowed to be followed by anything. + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid global variable."); + } + + return YP_TOKEN_GLOBAL_VARIABLE; + } + + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': + parser->current.end += yp_strspn_decimal_digit(parser->current.end, parser->end - parser->current.end); + return lex_state_p(parser, YP_LEX_STATE_FNAME) ? YP_TOKEN_GLOBAL_VARIABLE : YP_TOKEN_NUMBERED_REFERENCE; + + case '-': + parser->current.end++; + /* fallthrough */ + default: { + size_t width; + + if ((width = char_is_identifier(parser, parser->current.end)) > 0) { + do { + parser->current.end += width; + } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0); + } else { + // If we get here, then we have a $ followed by something that isn't + // recognized as a global variable. + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid global variable."); + } + + return YP_TOKEN_GLOBAL_VARIABLE; + } + } +} + +// This function checks if the current token matches a keyword. If it does, it +// returns true. Otherwise, it returns false. The arguments are as follows: +// +// * `value` - the literal string that we're checking for +// * `width` - the length of the token +// * `state` - the state that we should transition to if the token matches +// +static yp_token_type_t +lex_keyword(yp_parser_t *parser, const char *value, yp_lex_state_t state, yp_token_type_t type, yp_token_type_t modifier_type) { + yp_lex_state_t last_state = parser->lex_state; + + if (strncmp(parser->current.start, value, strlen(value)) == 0) { + if (parser->lex_state & YP_LEX_STATE_FNAME) { + lex_state_set(parser, YP_LEX_STATE_ENDFN); + } else { + lex_state_set(parser, state); + if (state == YP_LEX_STATE_BEG) { + parser->command_start = true; + } + + if ((modifier_type != YP_TOKEN_EOF) && !(last_state & (YP_LEX_STATE_BEG | YP_LEX_STATE_LABELED | YP_LEX_STATE_CLASS))) { + lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); + return modifier_type; + } + } + + return type; + } + + return YP_TOKEN_EOF; +} + +static yp_token_type_t +lex_identifier(yp_parser_t *parser, bool previous_command_start) { + // Lex as far as we can into the current identifier. + size_t width; + while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0) { + parser->current.end += width; + } + + // Now cache the length of the identifier so that we can quickly compare it + // against known keywords. + width = (size_t) (parser->current.end - parser->current.start); + + if (parser->current.end < parser->end) { + if (((parser->current.end + 1 >= parser->end) || (parser->current.end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) { + // First we'll attempt to extend the identifier by a ! or ?. Then we'll + // check if we're returning the defined? keyword or just an identifier. + width++; + + if ( + ((lex_state_p(parser, YP_LEX_STATE_LABEL | YP_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) && + (peek(parser) == ':') && (peek_at(parser, 1) != ':') + ) { + // If we're in a position where we can accept a : at the end of an + // identifier, then we'll optionally accept it. + lex_state_set(parser, YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED); + (void) match(parser, ':'); + return YP_TOKEN_LABEL; + } + + if (parser->lex_state != YP_LEX_STATE_DOT) { + if (width == 8 && (lex_keyword(parser, "defined?", YP_LEX_STATE_ARG, YP_TOKEN_KEYWORD_DEFINED, YP_TOKEN_EOF) != YP_TOKEN_EOF)) { + return YP_TOKEN_KEYWORD_DEFINED; + } + } + + return YP_TOKEN_IDENTIFIER; + } else if (lex_state_p(parser, YP_LEX_STATE_FNAME) && peek_at(parser, 1) != '~' && peek_at(parser, 1) != '>' && (peek_at(parser, 1) != '=' || peek_at(parser, 2) == '>') && match(parser, '=')) { + // If we're in a position where we can accept a = at the end of an + // identifier, then we'll optionally accept it. + return YP_TOKEN_IDENTIFIER; + } + + if ( + ((lex_state_p(parser, YP_LEX_STATE_LABEL | YP_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) && + peek(parser) == ':' && peek_at(parser, 1) != ':' + ) { + // If we're in a position where we can accept a : at the end of an + // identifier, then we'll optionally accept it. + lex_state_set(parser, YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED); + (void) match(parser, ':'); + return YP_TOKEN_LABEL; + } + } + + if (parser->lex_state != YP_LEX_STATE_DOT) { + yp_token_type_t type; + + switch (width) { + case 2: + if (lex_keyword(parser, "do", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_DO, YP_TOKEN_EOF) != YP_TOKEN_EOF) { + if (yp_do_loop_stack_p(parser)) { + return YP_TOKEN_KEYWORD_DO_LOOP; + } + return YP_TOKEN_KEYWORD_DO; + } + + if ((type = lex_keyword(parser, "if", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_IF, YP_TOKEN_KEYWORD_IF_MODIFIER)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "in", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_IN, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "or", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_OR, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + break; + case 3: + if ((type = lex_keyword(parser, "and", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_AND, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "def", YP_LEX_STATE_FNAME, YP_TOKEN_KEYWORD_DEF, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "end", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_END, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "END", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_END_UPCASE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "for", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_FOR, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "nil", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_NIL, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "not", YP_LEX_STATE_ARG, YP_TOKEN_KEYWORD_NOT, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + break; + case 4: + if ((type = lex_keyword(parser, "case", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_CASE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "else", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "next", YP_LEX_STATE_MID, YP_TOKEN_KEYWORD_NEXT, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "redo", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_REDO, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "self", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_SELF, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "then", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_THEN, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "true", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_TRUE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "when", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_WHEN, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + break; + case 5: + if ((type = lex_keyword(parser, "alias", YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM, YP_TOKEN_KEYWORD_ALIAS, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "begin", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_BEGIN, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "BEGIN", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_BEGIN_UPCASE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "break", YP_LEX_STATE_MID, YP_TOKEN_KEYWORD_BREAK, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "class", YP_LEX_STATE_CLASS, YP_TOKEN_KEYWORD_CLASS, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "elsif", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_ELSIF, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "false", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_FALSE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "retry", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_RETRY, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "super", YP_LEX_STATE_ARG, YP_TOKEN_KEYWORD_SUPER, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "undef", YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM, YP_TOKEN_KEYWORD_UNDEF, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "until", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_UNTIL, YP_TOKEN_KEYWORD_UNTIL_MODIFIER)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "while", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_WHILE, YP_TOKEN_KEYWORD_WHILE_MODIFIER)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "yield", YP_LEX_STATE_ARG, YP_TOKEN_KEYWORD_YIELD, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + break; + case 6: + if ((type = lex_keyword(parser, "ensure", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "module", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_MODULE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "rescue", YP_LEX_STATE_MID, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_RESCUE_MODIFIER)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "return", YP_LEX_STATE_MID, YP_TOKEN_KEYWORD_RETURN, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "unless", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_UNLESS, YP_TOKEN_KEYWORD_UNLESS_MODIFIER)) != YP_TOKEN_EOF) return type; + break; + case 8: + if ((type = lex_keyword(parser, "__LINE__", YP_LEX_STATE_END, YP_TOKEN_KEYWORD___LINE__, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + if ((type = lex_keyword(parser, "__FILE__", YP_LEX_STATE_END, YP_TOKEN_KEYWORD___FILE__, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + break; + case 12: + if ((type = lex_keyword(parser, "__ENCODING__", YP_LEX_STATE_END, YP_TOKEN_KEYWORD___ENCODING__, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; + break; + } + } + + return parser->encoding.isupper_char(parser->current.start) ? YP_TOKEN_CONSTANT : YP_TOKEN_IDENTIFIER; +} + +// Returns true if the current token that the parser is considering is at the +// beginning of a line or the beginning of the source. +static bool +current_token_starts_line(yp_parser_t *parser) { + return (parser->current.start == parser->start) || (parser->current.start[-1] == '\n'); +} + +// When we hit a # while lexing something like a string, we need to potentially +// handle interpolation. This function performs that check. It returns a token +// type representing what it found. Those cases are: +// +// * YP_TOKEN_NOT_PROVIDED - No interpolation was found at this point. The +// caller should keep lexing. +// * YP_TOKEN_STRING_CONTENT - No interpolation was found at this point. The +// caller should return this token type. +// * YP_TOKEN_EMBEXPR_BEGIN - An embedded expression was found. The caller +// should return this token type. +// * YP_TOKEN_EMBVAR - An embedded variable was found. The caller should return +// this token type. +// +static yp_token_type_t +lex_interpolation(yp_parser_t *parser, const char *pound) { + // If there is no content following this #, then we're at the end of + // the string and we can safely return string content. + if (pound + 1 >= parser->end) { + parser->current.end = pound + 1; + return YP_TOKEN_STRING_CONTENT; + } + + // Now we'll check against the character the follows the #. If it constitutes + // valid interplation, we'll handle that, otherwise we'll return + // YP_TOKEN_NOT_PROVIDED. + switch (pound[1]) { + case '@': { + // In this case we may have hit an embedded instance or class variable. + if (pound + 2 >= parser->end) { + parser->current.end = pound + 1; + return YP_TOKEN_STRING_CONTENT; + } + + // If we're looking at a @ and there's another @, then we'll skip past the + // second @. + const char *variable = pound + 2; + if (*variable == '@' && pound + 3 < parser->end) variable++; + + if (char_is_identifier_start(parser, variable)) { + // At this point we're sure that we've either hit an embedded instance + // or class variable. In this case we'll first need to check if we've + // already consumed content. + if (pound > parser->current.start) { + parser->current.end = pound; + return YP_TOKEN_STRING_CONTENT; + } + + // Otherwise we need to return the embedded variable token + // and then switch to the embedded variable lex mode. + lex_mode_push(parser, (yp_lex_mode_t) { .mode = YP_LEX_EMBVAR }); + parser->current.end = pound + 1; + return YP_TOKEN_EMBVAR; + } + + // If we didn't get an valid interpolation, then this is just regular + // string content. This is like if we get "#@-". In this case the caller + // should keep lexing. + parser->current.end = variable; + return YP_TOKEN_NOT_PROVIDED; + } + case '$': + // In this case we may have hit an embedded global variable. If there's + // not enough room, then we'll just return string content. + if (pound + 2 >= parser->end) { + parser->current.end = pound + 1; + return YP_TOKEN_STRING_CONTENT; + } + + // This is the character that we're going to check to see if it is the + // start of an identifier that would indicate that this is a global + // variable. + const char *check = pound + 2; + + if (pound[2] == '-') { + if (pound + 3 >= parser->end) { + parser->current.end = pound + 2; + return YP_TOKEN_STRING_CONTENT; + } + + check++; + } + + // If the character that we're going to check is the start of an + // identifier, or we don't have a - and the character is a decimal number + // or a global name punctuation character, then we've hit an embedded + // global variable. + if ( + char_is_identifier_start(parser, check) || + (pound[2] != '-' && (yp_char_is_decimal_digit(pound[2]) || char_is_global_name_punctuation(pound[2]))) + ) { + // In this case we've hit an embedded global variable. First check to + // see if we've already consumed content. If we have, then we need to + // return that content as string content first. + if (pound > parser->current.start) { + parser->current.end = pound; + return YP_TOKEN_STRING_CONTENT; + } + + // Otherwise, we need to return the embedded variable token and switch + // to the embedded variable lex mode. + lex_mode_push(parser, (yp_lex_mode_t) { .mode = YP_LEX_EMBVAR }); + parser->current.end = pound + 1; + return YP_TOKEN_EMBVAR; + } + + // In this case we've hit a #$ that does not indicate a global variable. + // In this case we'll continue lexing past it. + parser->current.end = pound + 1; + return YP_TOKEN_NOT_PROVIDED; + case '{': + // In this case it's the start of an embedded expression. If we have + // already consumed content, then we need to return that content as string + // content first. + if (pound > parser->current.start) { + parser->current.end = pound; + return YP_TOKEN_STRING_CONTENT; + } + + parser->enclosure_nesting++; + + // Otherwise we'll skip past the #{ and begin lexing the embedded + // expression. + lex_mode_push(parser, (yp_lex_mode_t) { .mode = YP_LEX_EMBEXPR }); + parser->current.end = pound + 2; + parser->command_start = true; + yp_do_loop_stack_push(parser, false); + return YP_TOKEN_EMBEXPR_BEGIN; + default: + // In this case we've hit a # that doesn't constitute interpolation. We'll + // mark that by returning the not provided token type. This tells the + // consumer to keep lexing forward. + parser->current.end = pound + 1; + return YP_TOKEN_NOT_PROVIDED; + } +} + +// This function is responsible for lexing either a character literal or the ? +// operator. The supported character literals are described below. +// +// \a bell, ASCII 07h (BEL) +// \b backspace, ASCII 08h (BS) +// \t horizontal tab, ASCII 09h (TAB) +// \n newline (line feed), ASCII 0Ah (LF) +// \v vertical tab, ASCII 0Bh (VT) +// \f form feed, ASCII 0Ch (FF) +// \r carriage return, ASCII 0Dh (CR) +// \e escape, ASCII 1Bh (ESC) +// \s space, ASCII 20h (SPC) +// \\ backslash +// \nnn octal bit pattern, where nnn is 1-3 octal digits ([0-7]) +// \xnn hexadecimal bit pattern, where nn is 1-2 hexadecimal digits ([0-9a-fA-F]) +// \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F]) +// \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F]) +// \cx or \C-x control character, where x is an ASCII printable character +// \M-x meta character, where x is an ASCII printable character +// \M-\C-x meta control character, where x is an ASCII printable character +// \M-\cx same as above +// \c\M-x same as above +// \c? or \C-? delete, ASCII 7Fh (DEL) +// +static yp_token_type_t +lex_question_mark(yp_parser_t *parser) { + if (lex_state_end_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_BEG); + return YP_TOKEN_QUESTION_MARK; + } + + if (parser->current.end >= parser->end) { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Incomplete character syntax."); + return YP_TOKEN_CHARACTER_LITERAL; + } + + if (yp_char_is_whitespace(*parser->current.end)) { + lex_state_set(parser, YP_LEX_STATE_BEG); + return YP_TOKEN_QUESTION_MARK; + } + + lex_state_set(parser, YP_LEX_STATE_BEG); + + if (parser->current.start[1] == '\\') { + lex_state_set(parser, YP_LEX_STATE_END); + parser->current.end += yp_unescape_calculate_difference(parser->current.start + 1, parser->end, YP_UNESCAPE_ALL, true, &parser->error_list); + return YP_TOKEN_CHARACTER_LITERAL; + } else { + size_t encoding_width = parser->encoding.char_width(parser->current.end); + // We only want to return a character literal if there's exactly one + // alphanumeric character right after the `?` + if ( + !parser->encoding.alnum_char(parser->current.end) || + !parser->encoding.alnum_char(parser->current.end + encoding_width) + ) { + lex_state_set(parser, YP_LEX_STATE_END); + parser->current.end += encoding_width; + return YP_TOKEN_CHARACTER_LITERAL; + } + } + + return YP_TOKEN_QUESTION_MARK; +} + +// Lex a variable that starts with an @ sign (either an instance or class +// variable). +static yp_token_type_t +lex_at_variable(yp_parser_t *parser) { + yp_token_type_t type = match(parser, '@') ? YP_TOKEN_CLASS_VARIABLE : YP_TOKEN_INSTANCE_VARIABLE; + size_t width; + + if (parser->current.end < parser->end && (width = char_is_identifier_start(parser, parser->current.end)) > 0) { + parser->current.end += width; + + while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0) { + parser->current.end += width; + } + } else if (type == YP_TOKEN_CLASS_VARIABLE) { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Incomplete class variable."); + } else { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Incomplete instance variable."); + } + + // If we're lexing an embedded variable, then we need to pop back into the + // parent lex context. + if (parser->lex_modes.current->mode == YP_LEX_EMBVAR) { + lex_mode_pop(parser); + } + + return type; +} + +// Optionally call out to the lex callback if one is provided. +static inline void +parser_lex_callback(yp_parser_t *parser) { + if (parser->lex_callback) { + parser->lex_callback->callback(parser->lex_callback->data, parser, &parser->current); + } +} + +// Return a new comment node of the specified type. +static inline yp_comment_t * +parser_comment(yp_parser_t *parser, yp_comment_type_t type) { + yp_comment_t *comment = (yp_comment_t *) malloc(sizeof(yp_comment_t)); + if (comment == NULL) return NULL; + + *comment = (yp_comment_t) { + .type = type, + .start = parser->current.start, + .end = parser->current.end + }; + + return comment; +} + +// Lex out embedded documentation, and return when we have either hit the end of +// the file or the end of the embedded documentation. This calls the callback +// manually because only the lexer should see these tokens, not the parser. +static yp_token_type_t +lex_embdoc(yp_parser_t *parser) { + // First, lex out the EMBDOC_BEGIN token. + const char *newline = next_newline(parser->current.end, parser->end - parser->current.end); + + if (newline == NULL) { + parser->current.end = parser->end; + } else { + yp_newline_list_append(&parser->newline_list, newline); + parser->current.end = newline + 1; + } + + parser->current.type = YP_TOKEN_EMBDOC_BEGIN; + parser_lex_callback(parser); + + // Now, create a comment that is going to be attached to the parser. + yp_comment_t *comment = parser_comment(parser, YP_COMMENT_EMBDOC); + if (comment == NULL) return YP_TOKEN_EOF; + + // Now, loop until we find the end of the embedded documentation or the end of + // the file. + while (parser->current.end + 4 <= parser->end) { + parser->current.start = parser->current.end; + + // If we've hit the end of the embedded documentation then we'll return that + // token here. + if (strncmp(parser->current.end, "=end", 4) == 0 && + (parser->current.end + 4 == parser->end || yp_char_is_whitespace(parser->current.end[4]))) { + const char *newline = next_newline(parser->current.end, parser->end - parser->current.end); + + if (newline == NULL) { + parser->current.end = parser->end; + } else { + yp_newline_list_append(&parser->newline_list, newline); + parser->current.end = newline + 1; + } + + parser->current.type = YP_TOKEN_EMBDOC_END; + parser_lex_callback(parser); + + comment->end = parser->current.end; + yp_list_append(&parser->comment_list, (yp_list_node_t *) comment); + + return YP_TOKEN_EMBDOC_END; + } + + // Otherwise, we'll parse until the end of the line and return a line of + // embedded documentation. + const char *newline = next_newline(parser->current.end, parser->end - parser->current.end); + + if (newline == NULL) { + parser->current.end = parser->end; + } else { + yp_newline_list_append(&parser->newline_list, newline); + parser->current.end = newline + 1; + } + + parser->current.type = YP_TOKEN_EMBDOC_LINE; + parser_lex_callback(parser); + } + + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unterminated embdoc"); + + comment->end = parser->current.end; + yp_list_append(&parser->comment_list, (yp_list_node_t *) comment); + + return YP_TOKEN_EOF; +} + +// Set the current type to an ignored newline and then call the lex callback. +// This happens in a couple places depending on whether or not we have already +// lexed a comment. +static inline void +parser_lex_ignored_newline(yp_parser_t *parser) { + parser->current.type = YP_TOKEN_IGNORED_NEWLINE; + parser_lex_callback(parser); +} + +// This function will be called when a newline is encountered. In some newlines, +// we need to check if there is a heredoc or heredocs that we have already lexed +// the body of that we need to now skip past. That will be indicated by the +// heredoc_end field on the parser. +// +// If it is set, then we need to skip past the heredoc body and then clear the +// heredoc_end field. +static inline void +parser_flush_heredoc_end(yp_parser_t *parser) { + assert(parser->heredoc_end <= parser->end); + parser->next_start = parser->heredoc_end; + parser->heredoc_end = NULL; +} + +// This is a convenience macro that will set the current token type, call the +// lex callback, and then return from the parser_lex function. +#define LEX(token_type) parser->current.type = token_type; parser_lex_callback(parser); return + +// Called when the parser requires a new token. The parser maintains a moving +// window of two tokens at a time: parser.previous and parser.current. This +// function will move the current token into the previous token and then +// lex a new token into the current token. +static void +parser_lex(yp_parser_t *parser) { + assert(parser->current.end <= parser->end); + parser->previous = parser->current; + + // This value mirrors cmd_state from CRuby. + bool previous_command_start = parser->command_start; + parser->command_start = false; + + // This is used to communicate to the newline lexing function that we've + // already seen a comment. + bool lexed_comment = false; + + switch (parser->lex_modes.current->mode) { + case YP_LEX_DEFAULT: + case YP_LEX_EMBEXPR: + case YP_LEX_EMBVAR: + case YP_LEX_NUMERIC: + + // We have a specific named label here because we are going to jump back to + // this location in the event that we have lexed a token that should not be + // returned to the parser. This includes comments, ignored newlines, and + // invalid tokens of some form. + lex_next_token: { + // If we have the special next_start pointer set, then we're going to jump + // to that location and start lexing from there. + if (parser->next_start != NULL) { + parser->current.end = parser->next_start; + parser->next_start = NULL; + } + + // This value mirrors space_seen from CRuby. It tracks whether or not + // space has been eaten before the start of the next token. + bool space_seen = false; + + // First, we're going to skip past any whitespace at the front of the next + // token. + bool chomping = true; + while (parser->current.end < parser->end && chomping) { + switch (*parser->current.end) { + case ' ': + case '\t': + case '\f': + case '\v': + parser->current.end++; + space_seen = true; + break; + case '\r': + if (peek_at(parser, 1) == '\n') { + chomping = false; + } else { + parser->current.end++; + space_seen = true; + } + break; + case '\\': + if (peek_at(parser, 1) == '\n') { + yp_newline_list_append(&parser->newline_list, parser->current.end + 1); + parser->current.end += 2; + space_seen = true; + } else if (parser->current.end + 2 < parser->end && peek_at(parser, 1) == '\r' && peek_at(parser, 2) == '\n') { + yp_newline_list_append(&parser->newline_list, parser->current.end + 2); + parser->current.end += 3; + space_seen = true; + } else if (yp_char_is_inline_whitespace(*parser->current.end)) { + parser->current.end += 2; + } else { + chomping = false; + } + break; + default: + chomping = false; + break; + } + } + + // Next, we'll set to start of this token to be the current end. + parser->current.start = parser->current.end; + + // We'll check if we're at the end of the file. If we are, then we need to + // return the EOF token. + if (parser->current.end >= parser->end) { + LEX(YP_TOKEN_EOF); + } + + // Finally, we'll check the current character to determine the next token. + switch (*parser->current.end++) { + case '\0': // NUL or end of script + case '\004': // ^D + case '\032': // ^Z + parser->current.end--; + LEX(YP_TOKEN_EOF); + + case '#': { // comments + const char *ending = next_newline(parser->current.end, parser->end - parser->current.end); + while (ending && ending < parser->end && *ending != '\n') { + ending = next_newline(ending + 1, parser->end - ending); + } + + parser->current.end = ending == NULL ? parser->end : ending + 1; + parser->current.type = YP_TOKEN_COMMENT; + parser_lex_callback(parser); + + // If we found a comment while lexing, then we're going to add it to the + // list of comments in the file and keep lexing. + yp_comment_t *comment = parser_comment(parser, YP_COMMENT_INLINE); + yp_list_append(&parser->comment_list, (yp_list_node_t *) comment); + + if (parser->current.start == parser->encoding_comment_start) { + parser_lex_encoding_comment(parser); + } + + lexed_comment = true; + } + /* fallthrough */ + case '\r': { + // The only way you can have carriage returns in this particular loop + // is if you have a carriage return followed by a newline. In that + // case we'll just skip over the carriage return and continue lexing, + // in order to make it so that the newline token encapsulates both the + // carriage return and the newline. Note that we need to check that + // we haven't already lexed a comment here because that falls through + // into here as well. + if (!lexed_comment) parser->current.end++; + } + /* fallthrough */ + case '\n': { + if (parser->heredoc_end == NULL) { + yp_newline_list_append(&parser->newline_list, parser->current.end - 1); + } else { + parser_flush_heredoc_end(parser); + } + + // If this is an ignored newline, then we can continue lexing after + // calling the callback with the ignored newline token. + switch (lex_state_ignored_p(parser)) { + case YP_IGNORED_NEWLINE_NONE: + break; + case YP_IGNORED_NEWLINE_PATTERN: + if (parser->pattern_matching_newlines || parser->in_keyword_arg) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, YP_LEX_STATE_BEG); + parser->command_start = true; + parser->current.type = YP_TOKEN_NEWLINE; + return; + } + /* fallthrough */ + case YP_IGNORED_NEWLINE_ALL: + if (!lexed_comment) parser_lex_ignored_newline(parser); + lexed_comment = false; + goto lex_next_token; + } + + // Here we need to look ahead and see if there is a call operator + // (either . or &.) that starts the next line. If there is, then this + // is going to become an ignored newline and we're going to instead + // return the call operator. + const char *next_content = parser->next_start == NULL ? parser->current.end : parser->next_start; + next_content += yp_strspn_inline_whitespace(next_content, parser->end - next_content); + + if (next_content < parser->end) { + // If we hit a comment after a newline, then we're going to check + // if it's ignored or if it's followed by a method call ('.'). + // If it is, then we're going to call the + // callback with an ignored newline and then continue lexing. + // Otherwise we'll return a regular newline. + if (next_content[0] == '#') { + // Here we look for a "." or "&." following a "\n". + const char *following = next_newline(next_content, parser->end - next_content); + + while (following && (following < parser->end)) { + following++; + following += yp_strspn_inline_whitespace(following, parser->end - following); + + // If this is not followed by a comment, then we can break out + // of this loop. + if (*following != '#') break; + + // If there is a comment, then we need to find the end of the + // comment and continue searching from there. + following = next_newline(following, parser->end - following); + } + + // If the lex state was ignored, or we hit a '.' or a '&.', + // we will lex the ignored newline + if (lex_state_ignored_p(parser) || (following && ((following[0] == '.') || (following + 1 < parser->end && following[0] == '&' && following[1] == '.')))) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lexed_comment = false; + goto lex_next_token; + } + } + + // If we hit a . after a newline, then we're in a call chain and + // we need to return the call operator. + if (next_content[0] == '.') { + // To match ripper, we need to emit an ignored newline even though + // its a real newline in the case that we have a beginless range + // on a subsequent line. + if ((next_content + 1 < parser->end) && (next_content[1] == '.')) { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, YP_LEX_STATE_BEG); + parser->command_start = true; + parser->current.type = YP_TOKEN_NEWLINE; + return; + } + + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, YP_LEX_STATE_DOT); + parser->current.start = next_content; + parser->current.end = next_content + 1; + parser->next_start = NULL; + LEX(YP_TOKEN_DOT); + } + + // If we hit a &. after a newline, then we're in a call chain and + // we need to return the call operator. + if (next_content + 1 < parser->end && next_content[0] == '&' && next_content[1] == '.') { + if (!lexed_comment) parser_lex_ignored_newline(parser); + lex_state_set(parser, YP_LEX_STATE_DOT); + parser->current.start = next_content; + parser->current.end = next_content + 2; + parser->next_start = NULL; + LEX(YP_TOKEN_AMPERSAND_DOT); + } + } + + // At this point we know this is a regular newline, and we can set the + // necessary state and return the token. + lex_state_set(parser, YP_LEX_STATE_BEG); + parser->command_start = true; + parser->current.type = YP_TOKEN_NEWLINE; + if (!lexed_comment) parser_lex_callback(parser); + return; + } + + // , + case ',': + lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); + LEX(YP_TOKEN_COMMA); + + // ( + case '(': { + yp_token_type_t type = YP_TOKEN_PARENTHESIS_LEFT; + + if (space_seen && (lex_state_arg_p(parser) || parser->lex_state == (YP_LEX_STATE_END | YP_LEX_STATE_LABEL))) { + type = YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES; + } + + parser->enclosure_nesting++; + lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); + yp_do_loop_stack_push(parser, false); + LEX(type); + } + + // ) + case ')': + parser->enclosure_nesting--; + lex_state_set(parser, YP_LEX_STATE_ENDFN); + yp_do_loop_stack_pop(parser); + LEX(YP_TOKEN_PARENTHESIS_RIGHT); + + // ; + case ';': + lex_state_set(parser, YP_LEX_STATE_BEG); + parser->command_start = true; + LEX(YP_TOKEN_SEMICOLON); + + // [ [] []= + case '[': + parser->enclosure_nesting++; + yp_token_type_t type = YP_TOKEN_BRACKET_LEFT; + + if (lex_state_operator_p(parser)) { + if (match(parser, ']')) { + parser->enclosure_nesting--; + lex_state_set(parser, YP_LEX_STATE_ARG); + LEX(match(parser, '=') ? YP_TOKEN_BRACKET_LEFT_RIGHT_EQUAL : YP_TOKEN_BRACKET_LEFT_RIGHT); + } + + lex_state_set(parser, YP_LEX_STATE_ARG | YP_LEX_STATE_LABEL); + LEX(type); + } + + if (lex_state_beg_p(parser) || (lex_state_arg_p(parser) && (space_seen || lex_state_p(parser, YP_LEX_STATE_LABELED)))) { + type = YP_TOKEN_BRACKET_LEFT_ARRAY; + } + + lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); + yp_do_loop_stack_push(parser, false); + LEX(type); + + // ] + case ']': + parser->enclosure_nesting--; + lex_state_set(parser, YP_LEX_STATE_END); + yp_do_loop_stack_pop(parser); + LEX(YP_TOKEN_BRACKET_RIGHT); + + // { + case '{': { + yp_token_type_t type = YP_TOKEN_BRACE_LEFT; + + if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) { + // This { begins a lambda + parser->command_start = true; + lex_state_set(parser, YP_LEX_STATE_BEG); + type = YP_TOKEN_LAMBDA_BEGIN; + } else if (lex_state_p(parser, YP_LEX_STATE_LABELED)) { + // This { begins a hash literal + lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); + } else if (lex_state_p(parser, YP_LEX_STATE_ARG_ANY | YP_LEX_STATE_END | YP_LEX_STATE_ENDFN)) { + // This { begins a block + parser->command_start = true; + lex_state_set(parser, YP_LEX_STATE_BEG); + } else if (lex_state_p(parser, YP_LEX_STATE_ENDARG)) { + // This { begins a block on a command + parser->command_start = true; + lex_state_set(parser, YP_LEX_STATE_BEG); + } else { + // This { begins a hash literal + lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); + } + + parser->enclosure_nesting++; + parser->brace_nesting++; + yp_do_loop_stack_push(parser, false); + + LEX(type); + } + + // } + case '}': + parser->enclosure_nesting--; + yp_do_loop_stack_pop(parser); + + if ((parser->lex_modes.current->mode == YP_LEX_EMBEXPR) && (parser->brace_nesting == 0)) { + lex_mode_pop(parser); + LEX(YP_TOKEN_EMBEXPR_END); + } + + parser->brace_nesting--; + lex_state_set(parser, YP_LEX_STATE_END); + LEX(YP_TOKEN_BRACE_RIGHT); + + // * ** **= *= + case '*': { + if (match(parser, '*')) { + if (match(parser, '=')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_STAR_STAR_EQUAL); + } + + yp_token_type_t type = YP_TOKEN_STAR_STAR; + + if (lex_state_spcarg_p(parser, space_seen) || lex_state_beg_p(parser)) { + type = YP_TOKEN_USTAR_STAR; + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + lex_state_set(parser, YP_LEX_STATE_BEG); + } + + LEX(type); + } + + if (match(parser, '=')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_STAR_EQUAL); + } + + yp_token_type_t type = YP_TOKEN_STAR; + + if (lex_state_spcarg_p(parser, space_seen)) { + yp_diagnostic_list_append(&parser->warning_list, parser->current.start, parser->current.end, "`*' interpreted as argument prefix"); + type = YP_TOKEN_USTAR; + } else if (lex_state_beg_p(parser)) { + type = YP_TOKEN_USTAR; + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + lex_state_set(parser, YP_LEX_STATE_BEG); + } + + LEX(type); + } + + // ! != !~ !@ + case '!': + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + if (match(parser, '@')) { + LEX(YP_TOKEN_BANG); + } + } else { + lex_state_set(parser, YP_LEX_STATE_BEG); + } + + if (match(parser, '=')) { + LEX(YP_TOKEN_BANG_EQUAL); + } + + if (match(parser, '~')) { + LEX(YP_TOKEN_BANG_TILDE); + } + + LEX(YP_TOKEN_BANG); + + // = => =~ == === =begin + case '=': + if (current_token_starts_line(parser) && strncmp(parser->current.end, "begin", 5) == 0 && yp_char_is_whitespace(parser->current.end[5])) { + yp_token_type_t type = lex_embdoc(parser); + + if (type == YP_TOKEN_EOF) { + LEX(type); + } + + goto lex_next_token; + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + lex_state_set(parser, YP_LEX_STATE_BEG); + } + + if (match(parser, '>')) { + LEX(YP_TOKEN_EQUAL_GREATER); + } + + if (match(parser, '~')) { + LEX(YP_TOKEN_EQUAL_TILDE); + } + + if (match(parser, '=')) { + LEX(match(parser, '=') ? YP_TOKEN_EQUAL_EQUAL_EQUAL : YP_TOKEN_EQUAL_EQUAL); + } + + LEX(YP_TOKEN_EQUAL); + + // < << <<= <= <=> + case '<': + if (match(parser, '<')) { + if ( + !lex_state_p(parser, YP_LEX_STATE_DOT | YP_LEX_STATE_CLASS) && + !lex_state_end_p(parser) && + (!lex_state_p(parser, YP_LEX_STATE_ARG_ANY) || lex_state_p(parser, YP_LEX_STATE_LABELED) || space_seen) + ) { + const char *end = parser->current.end; + + yp_heredoc_quote_t quote = YP_HEREDOC_QUOTE_NONE; + yp_heredoc_indent_t indent = YP_HEREDOC_INDENT_NONE; + + if (match(parser, '-')) { + indent = YP_HEREDOC_INDENT_DASH; + } + else if (match(parser, '~')) { + indent = YP_HEREDOC_INDENT_TILDE; + } + + if (match(parser, '`')) { + quote = YP_HEREDOC_QUOTE_BACKTICK; + } + else if (match(parser, '"')) { + quote = YP_HEREDOC_QUOTE_DOUBLE; + } + else if (match(parser, '\'')) { + quote = YP_HEREDOC_QUOTE_SINGLE; + } + + const char *ident_start = parser->current.end; + size_t width = 0; + + if (parser->current.end >= parser->end) { + parser->current.end = end; + } else if (quote == YP_HEREDOC_QUOTE_NONE && (width = char_is_identifier(parser, parser->current.end)) == 0) { + parser->current.end = end; + } else { + if (quote == YP_HEREDOC_QUOTE_NONE) { + parser->current.end += width; + + while ((parser->current.end < parser->end) && (width = char_is_identifier(parser, parser->current.end))) { + parser->current.end += width; + } + } else { + // If we have quotes, then we're going to go until we find the + // end quote. + while ((parser->current.end < parser->end) && quote != (yp_heredoc_quote_t) (*parser->current.end)) { + parser->current.end++; + } + } + + size_t ident_length = (size_t) (parser->current.end - ident_start); + if (quote != YP_HEREDOC_QUOTE_NONE && !match(parser, quote)) { + // TODO: handle unterminated heredoc + } + + lex_mode_push(parser, (yp_lex_mode_t) { + .mode = YP_LEX_HEREDOC, + .as.heredoc = { + .ident_start = ident_start, + .ident_length = ident_length, + .next_start = parser->current.end, + .quote = quote, + .indent = indent + } + }); + + if (parser->heredoc_end == NULL) { + const char *body_start = next_newline(parser->current.end, parser->end - parser->current.end); + + if (body_start == NULL) { + // If there is no newline after the heredoc identifier, then + // this is not a valid heredoc declaration. In this case we + // will add an error, but we will still return a heredoc + // start. + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unterminated heredoc."); + body_start = parser->end; + } else { + // Otherwise, we want to indicate that the body of the + // heredoc starts on the character after the next newline. + yp_newline_list_append(&parser->newline_list, body_start); + body_start++; + } + + parser->next_start = body_start; + } else { + parser->next_start = parser->heredoc_end; + } + + LEX(YP_TOKEN_HEREDOC_START); + } + } + + if (match(parser, '=')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_LESS_LESS_EQUAL); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + if (lex_state_p(parser, YP_LEX_STATE_CLASS)) parser->command_start = true; + lex_state_set(parser, YP_LEX_STATE_BEG); + } + + LEX(YP_TOKEN_LESS_LESS); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + if (lex_state_p(parser, YP_LEX_STATE_CLASS)) parser->command_start = true; + lex_state_set(parser, YP_LEX_STATE_BEG); + } + + if (match(parser, '=')) { + if (match(parser, '>')) { + LEX(YP_TOKEN_LESS_EQUAL_GREATER); + } + + LEX(YP_TOKEN_LESS_EQUAL); + } + + LEX(YP_TOKEN_LESS); + + // > >> >>= >= + case '>': + if (match(parser, '>')) { + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + lex_state_set(parser, YP_LEX_STATE_BEG); + } + LEX(match(parser, '=') ? YP_TOKEN_GREATER_GREATER_EQUAL : YP_TOKEN_GREATER_GREATER); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + lex_state_set(parser, YP_LEX_STATE_BEG); + } + + LEX(match(parser, '=') ? YP_TOKEN_GREATER_EQUAL : YP_TOKEN_GREATER); + + // double-quoted string literal + case '"': { + bool label_allowed = (lex_state_p(parser, YP_LEX_STATE_LABEL | YP_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser); + lex_mode_push_string(parser, true, label_allowed, '\0', '"'); + LEX(YP_TOKEN_STRING_BEGIN); + } + + // xstring literal + case '`': { + if (lex_state_p(parser, YP_LEX_STATE_FNAME)) { + lex_state_set(parser, YP_LEX_STATE_ENDFN); + LEX(YP_TOKEN_BACKTICK); + } + + if (lex_state_p(parser, YP_LEX_STATE_DOT)) { + if (previous_command_start) { + lex_state_set(parser, YP_LEX_STATE_CMDARG); + } else { + lex_state_set(parser, YP_LEX_STATE_ARG); + } + + LEX(YP_TOKEN_BACKTICK); + } + + lex_mode_push_string(parser, true, false, '\0', '`'); + LEX(YP_TOKEN_BACKTICK); + } + + // single-quoted string literal + case '\'': { + bool label_allowed = (lex_state_p(parser, YP_LEX_STATE_LABEL | YP_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser); + lex_mode_push_string(parser, false, label_allowed, '\0', '\''); + LEX(YP_TOKEN_STRING_BEGIN); + } + + // ? character literal + case '?': + LEX(lex_question_mark(parser)); + + // & && &&= &= + case '&': + if (match(parser, '&')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + + if (match(parser, '=')) { + LEX(YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL); + } + + LEX(YP_TOKEN_AMPERSAND_AMPERSAND); + } + + if (match(parser, '=')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_AMPERSAND_EQUAL); + } + + if (match(parser, '.')) { + lex_state_set(parser, YP_LEX_STATE_DOT); + LEX(YP_TOKEN_AMPERSAND_DOT); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + lex_state_set(parser, YP_LEX_STATE_BEG); + } + + LEX(YP_TOKEN_AMPERSAND); + + // | || ||= |= + case '|': + if (match(parser, '|')) { + if (match(parser, '=')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_PIPE_PIPE_EQUAL); + } + + if (lex_state_p(parser, YP_LEX_STATE_BEG)) { + parser->current.end--; + LEX(YP_TOKEN_PIPE); + } + + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_PIPE_PIPE); + } + + if (match(parser, '=')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_PIPE_EQUAL); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); + } + + LEX(YP_TOKEN_PIPE); + + // + += +@ + case '+': { + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + + if (match(parser, '@')) { + LEX(YP_TOKEN_UPLUS); + } + + LEX(YP_TOKEN_PLUS); + } + + if (match(parser, '=')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_PLUS_EQUAL); + } + + bool spcarg = lex_state_spcarg_p(parser, space_seen); + if (spcarg) { + yp_diagnostic_list_append( + &parser->warning_list, + parser->current.start, + parser->current.end, + "ambiguous first argument; put parentheses or a space even after `+` operator" + ); + } + + if (lex_state_beg_p(parser) || spcarg) { + lex_state_set(parser, YP_LEX_STATE_BEG); + + if (yp_char_is_decimal_digit(peek(parser))) { + parser->current.end++; + yp_token_type_t type = lex_numeric(parser); + lex_state_set(parser, YP_LEX_STATE_END); + LEX(type); + } + + LEX(YP_TOKEN_UPLUS); + } + + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_PLUS); + } + + // - -= -@ + case '-': { + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + + if (match(parser, '@')) { + LEX(YP_TOKEN_UMINUS); + } + + LEX(YP_TOKEN_MINUS); + } + + if (match(parser, '=')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_MINUS_EQUAL); + } + + if (match(parser, '>')) { + lex_state_set(parser, YP_LEX_STATE_ENDFN); + LEX(YP_TOKEN_MINUS_GREATER); + } + + bool spcarg = lex_state_spcarg_p(parser, space_seen); + if (spcarg) { + yp_diagnostic_list_append( + &parser->warning_list, + parser->current.start, + parser->current.end, + "ambiguous first argument; put parentheses or a space even after `-` operator" + ); + } + + if (lex_state_beg_p(parser) || spcarg) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(yp_char_is_decimal_digit(peek(parser)) ? YP_TOKEN_UMINUS_NUM : YP_TOKEN_UMINUS); + } + + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_MINUS); + } + + // . .. ... + case '.': { + bool beg_p = lex_state_beg_p(parser); + + if (match(parser, '.')) { + if (match(parser, '.')) { + // If we're _not_ inside a range within default parameters + if ( + !context_p(parser, YP_CONTEXT_DEFAULT_PARAMS) && + context_p(parser, YP_CONTEXT_DEF_PARAMS) + ) { + if (lex_state_p(parser, YP_LEX_STATE_END)) { + lex_state_set(parser, YP_LEX_STATE_BEG); + } else { + lex_state_set(parser, YP_LEX_STATE_ENDARG); + } + LEX(YP_TOKEN_UDOT_DOT_DOT); + } + + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(beg_p ? YP_TOKEN_UDOT_DOT_DOT : YP_TOKEN_DOT_DOT_DOT); + } + + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(beg_p ? YP_TOKEN_UDOT_DOT : YP_TOKEN_DOT_DOT); + } + + lex_state_set(parser, YP_LEX_STATE_DOT); + LEX(YP_TOKEN_DOT); + } + + // integer + case '0': + case '1': + case '2': + case '3': + case '4': + case '5': + case '6': + case '7': + case '8': + case '9': { + yp_token_type_t type = lex_numeric(parser); + lex_state_set(parser, YP_LEX_STATE_END); + LEX(type); + } + + // :: symbol + case ':': + if (match(parser, ':')) { + if (lex_state_beg_p(parser) || lex_state_p(parser, YP_LEX_STATE_CLASS) || (lex_state_p(parser, YP_LEX_STATE_ARG_ANY) && space_seen)) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_UCOLON_COLON); + } + + lex_state_set(parser, YP_LEX_STATE_DOT); + LEX(YP_TOKEN_COLON_COLON); + } + + if (lex_state_end_p(parser) || yp_char_is_whitespace(*parser->current.end) || (*parser->current.end == '#')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_COLON); + } + + if ((*parser->current.end == '"') || (*parser->current.end == '\'')) { + lex_mode_push_string(parser, *parser->current.end == '"', false, '\0', *parser->current.end); + parser->current.end++; + } + + lex_state_set(parser, YP_LEX_STATE_FNAME); + LEX(YP_TOKEN_SYMBOL_BEGIN); + + // / /= + case '/': + if (lex_state_beg_p(parser)) { + lex_mode_push_regexp(parser, '\0', '/'); + LEX(YP_TOKEN_REGEXP_BEGIN); + } + + if (match(parser, '=')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_SLASH_EQUAL); + } + + if (lex_state_spcarg_p(parser, space_seen)) { + yp_diagnostic_list_append(&parser->warning_list, parser->current.start, parser->current.end, "ambiguity between regexp and two divisions: wrap regexp in parentheses or add a space after `/' operator"); + lex_mode_push_regexp(parser, '\0', '/'); + LEX(YP_TOKEN_REGEXP_BEGIN); + } + + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + lex_state_set(parser, YP_LEX_STATE_BEG); + } + + LEX(YP_TOKEN_SLASH); + + // ^ ^= + case '^': + if (lex_state_operator_p(parser)) { + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + lex_state_set(parser, YP_LEX_STATE_BEG); + } + LEX(match(parser, '=') ? YP_TOKEN_CARET_EQUAL : YP_TOKEN_CARET); + + // ~ ~@ + case '~': + if (lex_state_operator_p(parser)) { + (void) match(parser, '@'); + lex_state_set(parser, YP_LEX_STATE_ARG); + } else { + lex_state_set(parser, YP_LEX_STATE_BEG); + } + + LEX(YP_TOKEN_TILDE); + + // % %= %i %I %q %Q %w %W + case '%': { + // In a BEG state, if you encounter a % then you must be starting + // something. In this case if there is no subsequent character then + // we have an invalid token. + if (lex_state_beg_p(parser) && (parser->current.end >= parser->end)) { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "unexpected end of input"); + LEX(YP_TOKEN_STRING_BEGIN); + } + + if (!lex_state_beg_p(parser) && match(parser, '=')) { + lex_state_set(parser, YP_LEX_STATE_BEG); + LEX(YP_TOKEN_PERCENT_EQUAL); + } + else if( + lex_state_beg_p(parser) || + (lex_state_p(parser, YP_LEX_STATE_FITEM) && (*parser->current.end == 's')) || + lex_state_spcarg_p(parser, space_seen) + ) { + if (!parser->encoding.alnum_char(parser->current.end)) { + lex_mode_push_string(parser, true, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + + if (*parser->current.end == '\r') { + parser->current.end++; + } + + if (*parser->current.end == '\n') { + yp_newline_list_append(&parser->newline_list, parser->current.end); + } + + parser->current.end++; + LEX(YP_TOKEN_STRING_BEGIN); + } + + switch (*parser->current.end) { + case 'i': { + parser->current.end++; + lex_mode_push_list(parser, false, *parser->current.end++); + LEX(YP_TOKEN_PERCENT_LOWER_I); + } + case 'I': { + parser->current.end++; + lex_mode_push_list(parser, true, *parser->current.end++); + LEX(YP_TOKEN_PERCENT_UPPER_I); + } + case 'r': { + parser->current.end++; + lex_mode_push_regexp(parser, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + parser->current.end++; + LEX(YP_TOKEN_REGEXP_BEGIN); + } + case 'q': { + parser->current.end++; + lex_mode_push_string(parser, false, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + parser->current.end++; + LEX(YP_TOKEN_STRING_BEGIN); + } + case 'Q': { + parser->current.end++; + lex_mode_push_string(parser, true, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + parser->current.end++; + LEX(YP_TOKEN_STRING_BEGIN); + } + case 's': { + parser->current.end++; + lex_mode_push_string(parser, false, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + lex_state_set(parser, YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM); + parser->current.end++; + LEX(YP_TOKEN_SYMBOL_BEGIN); + } + case 'w': { + parser->current.end++; + lex_mode_push_list(parser, false, *parser->current.end++); + LEX(YP_TOKEN_PERCENT_LOWER_W); + } + case 'W': { + parser->current.end++; + lex_mode_push_list(parser, true, *parser->current.end++); + LEX(YP_TOKEN_PERCENT_UPPER_W); + } + case 'x': { + parser->current.end++; + lex_mode_push_string(parser, true, false, lex_mode_incrementor(*parser->current.end), lex_mode_terminator(*parser->current.end)); + parser->current.end++; + LEX(YP_TOKEN_PERCENT_LOWER_X); + } + default: + // If we get to this point, then we have a % that is completely + // unparseable. In this case we'll just drop it from the parser + // and skip past it and hope that the next token is something + // that we can parse. + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "invalid %% token"); + goto lex_next_token; + } + } + + lex_state_set(parser, lex_state_operator_p(parser) ? YP_LEX_STATE_ARG : YP_LEX_STATE_BEG); + LEX(YP_TOKEN_PERCENT); + } + + // global variable + case '$': { + yp_token_type_t type = lex_global_variable(parser); + + // If we're lexing an embedded variable, then we need to pop back into + // the parent lex context. + if (parser->lex_modes.current->mode == YP_LEX_EMBVAR) { + lex_mode_pop(parser); + } + + lex_state_set(parser, YP_LEX_STATE_END); + LEX(type); + } + + // instance variable, class variable + case '@': + lex_state_set(parser, parser->lex_state & YP_LEX_STATE_FNAME ? YP_LEX_STATE_ENDFN : YP_LEX_STATE_END); + LEX(lex_at_variable(parser)); + + default: { + if (*parser->current.start != '_') { + size_t width = char_is_identifier_start(parser, parser->current.start); + + // If this isn't the beginning of an identifier, then it's an invalid + // token as we've exhausted all of the other options. We'll skip past + // it and return the next token. + if (!width) { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid token."); + goto lex_next_token; + } + + parser->current.end = parser->current.start + width; + } + + yp_token_type_t type = lex_identifier(parser, previous_command_start); + + // If we've hit a __END__ and it was at the start of the line or the + // start of the file and it is followed by either a \n or a \r\n, then + // this is the last token of the file. + if ( + ((parser->current.end - parser->current.start) == 7) && + current_token_starts_line(parser) && + (strncmp(parser->current.start, "__END__", 7) == 0) && + (*parser->current.end == '\n' || (*parser->current.end == '\r' && parser->current.end[1] == '\n')) + ) { + parser->current.end = parser->end; + parser->current.type = YP_TOKEN___END__; + parser_lex_callback(parser); + + yp_comment_t *comment = parser_comment(parser, YP_COMMENT___END__); + yp_list_append(&parser->comment_list, (yp_list_node_t *) comment); + + LEX(YP_TOKEN_EOF); + } + + yp_lex_state_t last_state = parser->lex_state; + + if (type == YP_TOKEN_IDENTIFIER || type == YP_TOKEN_CONSTANT) { + if (lex_state_p(parser, YP_LEX_STATE_BEG_ANY | YP_LEX_STATE_ARG_ANY | YP_LEX_STATE_DOT)) { + if (previous_command_start) { + lex_state_set(parser, YP_LEX_STATE_CMDARG); + } else { + lex_state_set(parser, YP_LEX_STATE_ARG); + } + } else if (parser->lex_state == YP_LEX_STATE_FNAME) { + lex_state_set(parser, YP_LEX_STATE_ENDFN); + } else { + lex_state_set(parser, YP_LEX_STATE_END); + } + } + + if ( + !(last_state & (YP_LEX_STATE_DOT | YP_LEX_STATE_FNAME)) && + (type == YP_TOKEN_IDENTIFIER) && + ((yp_parser_local_depth(parser, &parser->current) != -1) || + token_is_numbered_parameter(parser->current.start, parser->current.end)) + ) { + lex_state_set(parser, YP_LEX_STATE_END | YP_LEX_STATE_LABEL); + } + + LEX(type); + } + } + } + case YP_LEX_LIST: + // First we'll set the beginning of the token. + parser->current.start = parser->current.end; + + // If there's any whitespace at the start of the list, then we're + // going to trim it off the beginning and create a new token. + size_t whitespace; + if ((whitespace = yp_strspn_whitespace_newlines(parser->current.end, parser->end - parser->current.end, &parser->newline_list)) > 0) { + parser->current.end += whitespace; + LEX(YP_TOKEN_WORDS_SEP); + } + + // We'll check if we're at the end of the file. If we are, then we + // need to return the EOF token. + if (parser->current.end >= parser->end) { + LEX(YP_TOKEN_EOF); + } + + // Here we'll get a list of the places where strpbrk should break, + // and then find the first one. + const char *breakpoints = parser->lex_modes.current->as.list.breakpoints; + const char *breakpoint = yp_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + + while (breakpoint != NULL) { + switch (*breakpoint) { + case '\0': + // If we hit a null byte, skip directly past it. + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + break; + case '\\': { + // If we hit escapes, then we need to treat the next token + // literally. In this case we'll skip past the next character and + // find the next breakpoint. + + yp_unescape_type_t unescape_type; + if (parser->lex_modes.current->as.list.interpolation) { + unescape_type = YP_UNESCAPE_ALL; + } else { + unescape_type = YP_UNESCAPE_MINIMAL; + } + size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, unescape_type, false, &parser->error_list); + + // If the result is an escaped newline, then we need to + // track that newline. + if (breakpoint[difference - 1] == '\n') { + yp_newline_list_append(&parser->newline_list, breakpoint + difference - 1); + } + + breakpoint = yp_strpbrk(parser, breakpoint + difference, breakpoints, parser->end - (breakpoint + difference)); + break; + } + case ' ': + case '\t': + case '\f': + case '\r': + case '\v': + case '\n': + // If we've hit whitespace, then we must have received content by + // now, so we can return an element of the list. + parser->current.end = breakpoint; + LEX(YP_TOKEN_STRING_CONTENT); + case '#': { + // if # is the terminator, we need to fall into the default case + if (parser->lex_modes.current->as.list.terminator != '#') { + yp_token_type_t type = lex_interpolation(parser, breakpoint); + if (type != YP_TOKEN_NOT_PROVIDED) { + LEX(type); + } + + // If we haven't returned at this point then we had something + // that looked like an interpolated class or instance variable + // like "#@" but wasn't actually. In this case we'll just skip + // to the next breakpoint. + breakpoint = yp_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + } + } + /* fallthrough */ + default: + if (*breakpoint == parser->lex_modes.current->as.list.incrementor) { + // If we've hit the incrementor, then we need to skip past it and + // find the next breakpoint. + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + parser->lex_modes.current->as.list.nesting++; + break; + } + + // In this case we've hit the terminator. + assert(*breakpoint == parser->lex_modes.current->as.list.terminator); + + // If this terminator doesn't actually close the list, then we need + // to continue on past it. + if (parser->lex_modes.current->as.list.nesting > 0) { + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + parser->lex_modes.current->as.list.nesting--; + break; + } + + // If we've hit the terminator and we've already skipped past + // content, then we can return a list node. + if (breakpoint > parser->current.start) { + parser->current.end = breakpoint; + LEX(YP_TOKEN_STRING_CONTENT); + } + + // Otherwise, switch back to the default state and return the end of + // the list. + parser->current.end = breakpoint + 1; + lex_mode_pop(parser); + + lex_state_set(parser, YP_LEX_STATE_END); + LEX(YP_TOKEN_STRING_END); + } + } + + // If we were unable to find a breakpoint, then this token hits the end of + // the file. + LEX(YP_TOKEN_EOF); + + case YP_LEX_REGEXP: { + // First, we'll set to start of this token to be the current end. + parser->current.start = parser->current.end; + + // We'll check if we're at the end of the file. If we are, then we need to + // return the EOF token. + if (parser->current.end >= parser->end) { + LEX(YP_TOKEN_EOF); + } + + // These are the places where we need to split up the content of the + // regular expression. We'll use strpbrk to find the first of these + // characters. + const char *breakpoints = parser->lex_modes.current->as.regexp.breakpoints; + const char *breakpoint = yp_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + + while (breakpoint != NULL) { + switch (*breakpoint) { + case '\0': + // If we hit a null byte, skip directly past it. + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + break; + case '\\': { + // If we hit escapes, then we need to treat the next token + // literally. In this case we'll skip past the next character and + // find the next breakpoint. + size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, YP_UNESCAPE_ALL, false, &parser->error_list); + + // If the result is an escaped newline, then we need to + // track that newline. + if (breakpoint[difference - 1] == '\n') { + yp_newline_list_append(&parser->newline_list, breakpoint + difference - 1); + } + + breakpoint = yp_strpbrk(parser, breakpoint + difference, breakpoints, parser->end - (breakpoint + difference)); + break; + } + case '#': { + yp_token_type_t type = lex_interpolation(parser, breakpoint); + if (type != YP_TOKEN_NOT_PROVIDED) { + LEX(type); + } + + // We need to check if the terminator was # before skipping over + // to the next breakpoint + if (parser->lex_modes.current->as.regexp.terminator != '#') { + // If we haven't returned at this point then we had something + // that looked like an interpolated class or instance variable + // like "#@" but wasn't actually. In this case we'll just skip + // to the next breakpoint. + breakpoint = yp_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + } + } + /* fallthrough */ + default: { + if (*breakpoint == parser->lex_modes.current->as.regexp.incrementor) { + // If we've hit the incrementor, then we need to skip past it and + // find the next breakpoint. + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + parser->lex_modes.current->as.regexp.nesting++; + break; + } + + if (*breakpoint == '\n') { + // If we've hit a newline, then we need to track + // that in the list of newlines. + yp_newline_list_append(&parser->newline_list, breakpoint); + + if (parser->lex_modes.current->as.regexp.terminator != '\n') { + // If the terminator is not a newline, then we + // can set the next breakpoint and continue. + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + break; + } + + // Otherwise, the newline character is the + // terminator so we need to continue on. + } + + assert(*breakpoint == parser->lex_modes.current->as.regexp.terminator); + + if (parser->lex_modes.current->as.regexp.nesting > 0) { + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + parser->lex_modes.current->as.regexp.nesting--; + break; + } + + // Here we've hit the terminator. If we have already consumed + // content then we need to return that content as string content + // first. + if (breakpoint > parser->current.start) { + parser->current.end = breakpoint; + LEX(YP_TOKEN_STRING_CONTENT); + } + + // Since we've hit the terminator of the regular expression, we now + // need to parse the options. + parser->current.end = breakpoint + 1; + parser->current.end += yp_strspn_regexp_option(parser->current.end, parser->end - parser->current.end); + + lex_mode_pop(parser); + lex_state_set(parser, YP_LEX_STATE_END); + LEX(YP_TOKEN_REGEXP_END); + } + } + } + + // At this point, the breakpoint is NULL which means we were unable to + // find anything before the end of the file. + LEX(YP_TOKEN_EOF); + } + case YP_LEX_STRING: { + // First, we'll set to start of this token to be the current end. + if (parser->next_start == NULL) { + parser->current.start = parser->current.end; + } else { + parser->current.start = parser->next_start; + parser->current.end = parser->next_start; + parser->next_start = NULL; + } + + // We'll check if we're at the end of the file. If we are, then we need to + // return the EOF token. + if (parser->current.end >= parser->end) { + LEX(YP_TOKEN_EOF); + } + + // These are the places where we need to split up the content of the + // string. We'll use strpbrk to find the first of these characters. + const char *breakpoints = parser->lex_modes.current->as.string.breakpoints; + const char *breakpoint = yp_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + + while (breakpoint != NULL) { + // If we hit the incrementor, then we'll increment then nesting and + // continue lexing. + if ( + parser->lex_modes.current->as.string.incrementor != '\0' && + *breakpoint == parser->lex_modes.current->as.string.incrementor + ) { + parser->lex_modes.current->as.string.nesting++; + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + continue; + } + + // Note that we have to check the terminator here first because we could + // potentially be parsing a % string that has a # character as the + // terminator. + if (*breakpoint == parser->lex_modes.current->as.string.terminator) { + // If this terminator doesn't actually close the string, then we need + // to continue on past it. + if (parser->lex_modes.current->as.string.nesting > 0) { + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + parser->lex_modes.current->as.string.nesting--; + continue; + } + + // Here we've hit the terminator. If we have already consumed content + // then we need to return that content as string content first. + if (breakpoint > parser->current.start) { + parser->current.end = breakpoint; + LEX(YP_TOKEN_STRING_CONTENT); + } + + // Otherwise we need to switch back to the parent lex mode and + // return the end of the string. + if (*parser->current.end == '\r' && parser->current.end + 1 < parser->end && parser->current.end[1] == '\n') { + parser->current.end = breakpoint + 2; + yp_newline_list_append(&parser->newline_list, breakpoint + 1); + } else { + if (*parser->current.end == '\n') { + yp_newline_list_append(&parser->newline_list, parser->current.end); + } + + parser->current.end = breakpoint + 1; + } + + if ( + parser->lex_modes.current->as.string.label_allowed && + (peek(parser) == ':') && + (peek_at(parser, 1) != ':') + ) { + parser->current.end++; + lex_state_set(parser, YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED); + lex_mode_pop(parser); + LEX(YP_TOKEN_LABEL_END); + } + + lex_state_set(parser, YP_LEX_STATE_END); + lex_mode_pop(parser); + LEX(YP_TOKEN_STRING_END); + } + + // When we hit a newline, we need to flush any potential heredocs. Note + // that this has to happen after we check for the terminator in case the + // terminator is a newline character. + if (*breakpoint == '\n') { + if (parser->heredoc_end == NULL) { + yp_newline_list_append(&parser->newline_list, breakpoint); + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + continue; + } else { + parser->current.end = breakpoint + 1; + parser_flush_heredoc_end(parser); + LEX(YP_TOKEN_STRING_CONTENT); + } + } + + switch (*breakpoint) { + case '\0': + // Skip directly past the null character. + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + break; + case '\\': { + // If we hit escapes, then we need to treat the next token + // literally. In this case we'll skip past the next character and + // find the next breakpoint. + yp_unescape_type_t unescape_type = parser->lex_modes.current->as.string.interpolation ? YP_UNESCAPE_ALL : YP_UNESCAPE_MINIMAL; + size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, unescape_type, false, &parser->error_list); + + // If the result is an escaped newline, then we need to + // track that newline. + if (breakpoint[difference - 1] == '\n') { + yp_newline_list_append(&parser->newline_list, breakpoint + difference - 1); + } + + breakpoint = yp_strpbrk(parser, breakpoint + difference, breakpoints, parser->end - (breakpoint + difference)); + break; + } + case '#': { + yp_token_type_t type = lex_interpolation(parser, breakpoint); + if (type != YP_TOKEN_NOT_PROVIDED) { + LEX(type); + } + + // If we haven't returned at this point then we had something that + // looked like an interpolated class or instance variable like "#@" + // but wasn't actually. In this case we'll just skip to the next + // breakpoint. + breakpoint = yp_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + } + default: + assert(false && "unreachable"); + } + } + + // If we've hit the end of the string, then this is an unterminated + // string. In that case we'll return the EOF token. + parser->current.end = parser->end; + LEX(YP_TOKEN_EOF); + } + case YP_LEX_HEREDOC: { + // First, we'll set to start of this token. + if (parser->next_start == NULL) { + parser->current.start = parser->current.end; + } else { + parser->current.start = parser->next_start; + parser->current.end = parser->next_start; + parser->next_start = NULL; + } + + // We'll check if we're at the end of the file. If we are, then we need to + // return the EOF token. + if (parser->current.end >= parser->end) { + LEX(YP_TOKEN_EOF); + } + + // Now let's grab the information about the identifier off of the current + // lex mode. + const char *ident_start = parser->lex_modes.current->as.heredoc.ident_start; + size_t ident_length = parser->lex_modes.current->as.heredoc.ident_length; + + // If we are immediately following a newline and we have hit the + // terminator, then we need to return the ending of the heredoc. + if (parser->current.start[-1] == '\n') { + const char *start = parser->current.start; + if (parser->lex_modes.current->as.heredoc.indent != YP_HEREDOC_INDENT_NONE) { + start += yp_strspn_inline_whitespace(start, parser->end - start); + } + + if (strncmp(start, ident_start, ident_length) == 0) { + bool matched = true; + bool at_end = false; + + if ((start + ident_length < parser->end) && (start[ident_length] == '\n')) { + parser->current.end = start + ident_length + 1; + yp_newline_list_append(&parser->newline_list, start + ident_length); + } else if ((start + ident_length + 1 < parser->end) && (start[ident_length] == '\r') && (start[ident_length + 1] == '\n')) { + parser->current.end = start + ident_length + 2; + yp_newline_list_append(&parser->newline_list, start + ident_length + 1); + } else if (parser->end == (start + ident_length)) { + parser->current.end = start + ident_length; + at_end = true; + } else { + matched = false; + } + + if (matched) { + if (*parser->lex_modes.current->as.heredoc.next_start == '\\') { + parser->next_start = NULL; + } else { + parser->next_start = parser->lex_modes.current->as.heredoc.next_start; + parser->heredoc_end = parser->current.end; + } + + lex_mode_pop(parser); + if (!at_end) { + lex_state_set(parser, YP_LEX_STATE_END); + } + LEX(YP_TOKEN_HEREDOC_END); + } + } + } + + // Otherwise we'll be parsing string content. These are the places where + // we need to split up the content of the heredoc. We'll use strpbrk to + // find the first of these characters. + char breakpoints[] = "\n\\#"; + + yp_heredoc_quote_t quote = parser->lex_modes.current->as.heredoc.quote; + if (quote == YP_HEREDOC_QUOTE_SINGLE) { + breakpoints[2] = '\0'; + } + + const char *breakpoint = yp_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + + while (breakpoint != NULL) { + switch (*breakpoint) { + case '\0': + // Skip directly past the null character. + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + break; + case '\n': { + yp_newline_list_append(&parser->newline_list, breakpoint); + + if (parser->heredoc_end != NULL && (parser->heredoc_end > breakpoint)) { + parser_flush_heredoc_end(parser); + parser->current.end = breakpoint + 1; + LEX(YP_TOKEN_STRING_CONTENT); + } + + const char *start = breakpoint + 1; + if (parser->lex_modes.current->as.heredoc.indent != YP_HEREDOC_INDENT_NONE) { + start += yp_strspn_inline_whitespace(start, parser->end - start); + } + + // If we have hit a newline that is followed by a valid terminator, + // then we need to return the content of the heredoc here as string + // content. Then, the next time a token is lexed, it will match + // again and return the end of the heredoc. + if ( + (start + ident_length <= parser->end) && + (strncmp(start, ident_start, ident_length) == 0) + ) { + // Heredoc terminators must be followed by a newline or EOF to be valid. + if (start + ident_length == parser->end || start[ident_length] == '\n') { + parser->current.end = breakpoint + 1; + LEX(YP_TOKEN_STRING_CONTENT); + } + + // They can also be followed by a carriage return and then a + // newline. Be sure here that we don't accidentally read off the + // end. + if ( + (start + ident_length + 1 < parser->end) && + (start[ident_length] == '\r') && + (start[ident_length + 1] == '\n') + ) { + parser->current.end = breakpoint + 1; + LEX(YP_TOKEN_STRING_CONTENT); + } + } + + // Otherwise we hit a newline and it wasn't followed by a + // terminator, so we can continue parsing. + breakpoint = yp_strpbrk(parser, breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); + break; + } + case '\\': { + // If we hit escapes, then we need to treat the next token + // literally. In this case we'll skip past the next character and + // find the next breakpoint. + if (breakpoint[1] == '\n') { + breakpoint++; + } else { + yp_unescape_type_t unescape_type = (quote == YP_HEREDOC_QUOTE_SINGLE) ? YP_UNESCAPE_MINIMAL : YP_UNESCAPE_ALL; + size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, unescape_type, false, &parser->error_list); + + if (breakpoint[difference - 1] == '\n') { + yp_newline_list_append(&parser->newline_list, breakpoint + difference - 1); + } + + breakpoint = yp_strpbrk(parser, breakpoint + difference, breakpoints, parser->end - (breakpoint + difference)); + } + break; + } + case '#': { + yp_token_type_t type = lex_interpolation(parser, breakpoint); + if (type != YP_TOKEN_NOT_PROVIDED) { + LEX(type); + } + + // If we haven't returned at this point then we had something + // that looked like an interpolated class or instance variable + // like "#@" but wasn't actually. In this case we'll just skip + // to the next breakpoint. + breakpoint = yp_strpbrk(parser, parser->current.end, breakpoints, parser->end - parser->current.end); + break; + } + default: + assert(false && "unreachable"); + } + } + + // If we've hit the end of the string, then this is an unterminated + // heredoc. In that case we'll return the EOF token. + parser->current.end = parser->end; + LEX(YP_TOKEN_EOF); + } + } + + assert(false && "unreachable"); +} + +#undef LEX + +/******************************************************************************/ +/* Parse functions */ +/******************************************************************************/ + +// When we are parsing certain content, we need to unescape the content to +// provide to the consumers of the parser. The following functions accept a range +// of characters from the source and unescapes into the provided type. +// +// We have functions for unescaping regular expression nodes, string nodes, +// symbol nodes, and xstring nodes +static yp_regular_expression_node_t * +yp_regular_expression_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing, yp_unescape_type_t unescape_type) { + yp_regular_expression_node_t *node = yp_regular_expression_node_create(parser, opening, content, closing); + + ptrdiff_t length = content->end - content->start; + assert(length >= 0); + + yp_unescape_manipulate_string(parser, content->start, (size_t) length, &node->unescaped, unescape_type, &parser->error_list); + return node; +} + +static yp_symbol_node_t * +yp_symbol_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing, yp_unescape_type_t unescape_type) { + yp_symbol_node_t *node = yp_symbol_node_create(parser, opening, content, closing); + + ptrdiff_t length = content->end - content->start; + assert(length >= 0); + + yp_unescape_manipulate_string(parser, content->start, (size_t) length, &node->unescaped, unescape_type, &parser->error_list); + return node; +} + +static yp_string_node_t * +yp_string_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing, yp_unescape_type_t unescape_type) { + yp_string_node_t *node = yp_string_node_create(parser, opening, content, closing); + + ptrdiff_t length = content->end - content->start; + assert(length >= 0); + + yp_unescape_manipulate_string(parser, content->start, (size_t) length, &node->unescaped, unescape_type, &parser->error_list); + return node; +} + +static yp_x_string_node_t * +yp_xstring_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing) { + yp_x_string_node_t *node = yp_xstring_node_create(parser, opening, content, closing); + + ptrdiff_t length = content->end - content->start; + assert(length >= 0); + + yp_unescape_manipulate_string(parser, content->start, (size_t) length, &node->unescaped, YP_UNESCAPE_ALL, &parser->error_list); + return node; +} + +// Returns true if the current token is of the specified type. +static inline bool +match_type_p(yp_parser_t *parser, yp_token_type_t type) { + return parser->current.type == type; +} + +// Returns true if the current token is of any of the specified types. +static bool +match_any_type_p(yp_parser_t *parser, size_t count, ...) { + va_list types; + va_start(types, count); + + for (size_t index = 0; index < count; index++) { + if (match_type_p(parser, va_arg(types, yp_token_type_t))) { + va_end(types); + return true; + } + } + + va_end(types); + return false; +} + +// These are the various precedence rules. Because we are using a Pratt parser, +// they are named binding power to represent the manner in which nodes are bound +// together in the stack. +// +// We increment by 2 because we want to leave room for the infix operators to +// specify their associativity by adding or subtracting one. +typedef enum { + YP_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator + YP_BINDING_POWER_STATEMENT = 2, + YP_BINDING_POWER_MODIFIER = 4, // if unless until while in + YP_BINDING_POWER_MODIFIER_RESCUE = 6, // rescue + YP_BINDING_POWER_COMPOSITION = 8, // and or + YP_BINDING_POWER_NOT = 10, // not + YP_BINDING_POWER_MATCH = 12, // => + YP_BINDING_POWER_DEFINED = 14, // defined? + YP_BINDING_POWER_ASSIGNMENT = 16, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **= + YP_BINDING_POWER_TERNARY = 18, // ?: + YP_BINDING_POWER_RANGE = 20, // .. ... + YP_BINDING_POWER_LOGICAL_OR = 22, // || + YP_BINDING_POWER_LOGICAL_AND = 24, // && + YP_BINDING_POWER_EQUALITY = 26, // <=> == === != =~ !~ + YP_BINDING_POWER_COMPARISON = 28, // > >= < <= + YP_BINDING_POWER_BITWISE_OR = 30, // | ^ + YP_BINDING_POWER_BITWISE_AND = 32, // & + YP_BINDING_POWER_SHIFT = 34, // << >> + YP_BINDING_POWER_TERM = 36, // + - + YP_BINDING_POWER_FACTOR = 38, // * / % + YP_BINDING_POWER_UMINUS = 40, // -@ + YP_BINDING_POWER_EXPONENT = 42, // ** + YP_BINDING_POWER_UNARY = 44, // ! ~ +@ + YP_BINDING_POWER_INDEX = 46, // [] []= + YP_BINDING_POWER_CALL = 48, // :: . + YP_BINDING_POWER_MAX = 50 +} yp_binding_power_t; + +// This struct represents a set of binding powers used for a given token. They +// are combined in this way to make it easier to represent associativity. +typedef struct { + yp_binding_power_t left; + yp_binding_power_t right; + bool binary; +} yp_binding_powers_t; + +#define BINDING_POWER_ASSIGNMENT { YP_BINDING_POWER_UNARY, YP_BINDING_POWER_ASSIGNMENT, true } +#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true } +#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true } +#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false } + +yp_binding_powers_t yp_binding_powers[YP_TOKEN_MAXIMUM] = { + // if unless until while in rescue + [YP_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MODIFIER), + [YP_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MODIFIER), + [YP_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MODIFIER), + [YP_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MODIFIER), + [YP_TOKEN_KEYWORD_IN] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MODIFIER), + + // rescue modifier + [YP_TOKEN_KEYWORD_RESCUE_MODIFIER] = { + YP_BINDING_POWER_ASSIGNMENT, + YP_BINDING_POWER_MODIFIER_RESCUE + 1, + true + }, + + // and or + [YP_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_COMPOSITION), + [YP_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_COMPOSITION), + + // => + [YP_TOKEN_EQUAL_GREATER] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MATCH), + + // &&= &= ^= = >>= <<= -= %= |= += /= *= **= + [YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_CARET_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_GREATER_GREATER_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_LESS_LESS_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_MINUS_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_PERCENT_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_PIPE_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_PLUS_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_SLASH_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT, + [YP_TOKEN_STAR_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT, + + // ?: + [YP_TOKEN_QUESTION_MARK] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_TERNARY), + + // .. ... + [YP_TOKEN_DOT_DOT] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_RANGE), + [YP_TOKEN_DOT_DOT_DOT] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_RANGE), + + // || + [YP_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_LOGICAL_OR), + + // && + [YP_TOKEN_AMPERSAND_AMPERSAND] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_LOGICAL_AND), + + // != !~ == === =~ <=> + [YP_TOKEN_BANG_EQUAL] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), + [YP_TOKEN_BANG_TILDE] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), + [YP_TOKEN_EQUAL_EQUAL] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), + [YP_TOKEN_EQUAL_EQUAL_EQUAL] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), + [YP_TOKEN_EQUAL_TILDE] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), + [YP_TOKEN_LESS_EQUAL_GREATER] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), + + // > >= < <= + [YP_TOKEN_GREATER] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_COMPARISON), + [YP_TOKEN_GREATER_EQUAL] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_COMPARISON), + [YP_TOKEN_LESS] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_COMPARISON), + [YP_TOKEN_LESS_EQUAL] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_COMPARISON), + + // ^ | + [YP_TOKEN_CARET] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_BITWISE_OR), + [YP_TOKEN_PIPE] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_BITWISE_OR), + + // & + [YP_TOKEN_AMPERSAND] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_BITWISE_AND), + + // >> << + [YP_TOKEN_GREATER_GREATER] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_SHIFT), + [YP_TOKEN_LESS_LESS] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_SHIFT), + + // - + + [YP_TOKEN_MINUS] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_TERM), + [YP_TOKEN_PLUS] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_TERM), + + // % / * + [YP_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_FACTOR), + [YP_TOKEN_SLASH] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_FACTOR), + [YP_TOKEN_STAR] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_FACTOR), + [YP_TOKEN_USTAR] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_FACTOR), + + // -@ + [YP_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UMINUS), + [YP_TOKEN_UMINUS_NUM] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UMINUS), + + // ** + [YP_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EXPONENT), + [YP_TOKEN_USTAR_STAR] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UNARY), + + // ! ~ +@ + [YP_TOKEN_BANG] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UNARY), + [YP_TOKEN_TILDE] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UNARY), + [YP_TOKEN_UPLUS] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UNARY), + + // [ + [YP_TOKEN_BRACKET_LEFT] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_INDEX), + + // :: . &. + [YP_TOKEN_COLON_COLON] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_CALL), + [YP_TOKEN_DOT] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_CALL), + [YP_TOKEN_AMPERSAND_DOT] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_CALL) +}; + +#undef BINDING_POWER_ASSIGNMENT +#undef LEFT_ASSOCIATIVE +#undef RIGHT_ASSOCIATIVE +#undef RIGHT_ASSOCIATIVE_UNARY + +// If the current token is of the specified type, lex forward by one token and +// return true. Otherwise, return false. For example: +// +// if (accept(parser, YP_TOKEN_COLON)) { ... } +// +static bool +accept(yp_parser_t *parser, yp_token_type_t type) { + if (match_type_p(parser, type)) { + parser_lex(parser); + return true; + } + return false; +} + +// If the current token is of any of the specified types, lex forward by one +// token and return true. Otherwise, return false. For example: +// +// if (accept_any(parser, 2, YP_TOKEN_COLON, YP_TOKEN_SEMICOLON)) { ... } +// +static bool +accept_any(yp_parser_t *parser, size_t count, ...) { + va_list types; + va_start(types, count); + + for (size_t index = 0; index < count; index++) { + if (match_type_p(parser, va_arg(types, yp_token_type_t))) { + parser_lex(parser); + va_end(types); + return true; + } + } + + va_end(types); + return false; +} + +// This function indicates that the parser expects a token in a specific +// position. For example, if you're parsing a BEGIN block, you know that a { is +// expected immediately after the keyword. In that case you would call this +// function to indicate that that token should be found. +// +// If we didn't find the token that we were expecting, then we're going to add +// an error to the parser's list of errors (to indicate that the tree is not +// valid) and create an artificial token instead. This allows us to recover from +// the fact that the token isn't present and continue parsing. +static void +expect(yp_parser_t *parser, yp_token_type_t type, const char *message) { + if (accept(parser, type)) return; + + yp_diagnostic_list_append(&parser->error_list, parser->previous.end, parser->previous.end, message); + + parser->previous = + (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; +} + +static void +expect_any(yp_parser_t *parser, const char*message, size_t count, ...) { + va_list types; + va_start(types, count); + + for (size_t index = 0; index < count; index++) { + if (accept(parser, va_arg(types, yp_token_type_t))) { + va_end(types); + return; + } + } + + va_end(types); + + yp_diagnostic_list_append(&parser->error_list, parser->previous.end, parser->previous.end, message); + parser->previous = + (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; +} + +static yp_node_t * +parse_expression(yp_parser_t *parser, yp_binding_power_t binding_power, const char *message); + +// This function controls whether or not we will attempt to parse an expression +// beginning at the subsequent token. It is used when we are in a context where +// an expression is optional. +// +// For example, looking at a range object when we've already lexed the operator, +// we need to know if we should attempt to parse an expression on the right. +// +// For another example, if we've parsed an identifier or a method call and we do +// not have parentheses, then the next token may be the start of an argument or +// it may not. +// +// CRuby parsers that are generated would resolve this by using a lookahead and +// potentially backtracking. We attempt to do this by just looking at the next +// token and making a decision based on that. I am not sure if this is going to +// work in all cases, it may need to be refactored later. But it appears to work +// for now. +static inline bool +token_begins_expression_p(yp_token_type_t type) { + switch (type) { + case YP_TOKEN_EQUAL_GREATER: + case YP_TOKEN_KEYWORD_IN: + // We need to special case this because it is a binary operator that + // should not be marked as beginning an expression. + return false; + case YP_TOKEN_BRACE_RIGHT: + case YP_TOKEN_BRACKET_RIGHT: + case YP_TOKEN_COLON: + case YP_TOKEN_COMMA: + case YP_TOKEN_EMBEXPR_END: + case YP_TOKEN_EOF: + case YP_TOKEN_LAMBDA_BEGIN: + case YP_TOKEN_KEYWORD_DO: + case YP_TOKEN_KEYWORD_DO_LOOP: + case YP_TOKEN_KEYWORD_END: + case YP_TOKEN_KEYWORD_ELSE: + case YP_TOKEN_KEYWORD_ELSIF: + case YP_TOKEN_KEYWORD_THEN: + case YP_TOKEN_KEYWORD_RESCUE: + case YP_TOKEN_KEYWORD_WHEN: + case YP_TOKEN_NEWLINE: + case YP_TOKEN_PARENTHESIS_RIGHT: + case YP_TOKEN_SEMICOLON: + // The reason we need this short-circuit is because we're using the + // binding powers table to tell us if the subsequent token could + // potentially be the start of an expression . If there _is_ a binding + // power for one of these tokens, then we should remove it from this list + // and let it be handled by the default case below. + assert(yp_binding_powers[type].left == YP_BINDING_POWER_UNSET); + return false; + case YP_TOKEN_UCOLON_COLON: + case YP_TOKEN_UMINUS: + case YP_TOKEN_UMINUS_NUM: + case YP_TOKEN_UPLUS: + case YP_TOKEN_BANG: + case YP_TOKEN_TILDE: + case YP_TOKEN_UDOT_DOT: + case YP_TOKEN_UDOT_DOT_DOT: + // These unary tokens actually do have binding power associated with them + // so that we can correctly place them into the precedence order. But we + // want them to be marked as beginning an expression, so we need to + // special case them here. + return true; + default: + return yp_binding_powers[type].left == YP_BINDING_POWER_UNSET; + } +} + +// Parse an expression with the given binding power that may be optionally +// prefixed by the * operator. +static yp_node_t * +parse_starred_expression(yp_parser_t *parser, yp_binding_power_t binding_power, const char *message) { + if (accept(parser, YP_TOKEN_USTAR)) { + yp_token_t operator = parser->previous; + yp_node_t *expression = parse_expression(parser, binding_power, "Expected expression after `*'."); + return (yp_node_t *) yp_splat_node_create(parser, &operator, expression); + } + + return parse_expression(parser, binding_power, message); +} + +// Convert the given node into a valid target node. +static yp_node_t * +parse_target(yp_parser_t *parser, yp_node_t *target, yp_token_t *operator, yp_node_t *value) { + switch (target->type) { + case YP_NODE_MISSING_NODE: + return target; + case YP_NODE_CLASS_VARIABLE_READ_NODE: { + yp_class_variable_write_node_t *write_node = yp_class_variable_read_node_to_class_variable_write_node(parser, (yp_class_variable_read_node_t *) target, operator, value); + yp_node_destroy(parser, target); + return (yp_node_t *) write_node; + } + case YP_NODE_CONSTANT_PATH_NODE: + case YP_NODE_CONSTANT_READ_NODE: + return (yp_node_t *) yp_constant_path_write_node_create(parser, target, operator, value); + case YP_NODE_BACK_REFERENCE_READ_NODE: + case YP_NODE_NUMBERED_REFERENCE_READ_NODE: + yp_diagnostic_list_append(&parser->error_list, target->location.start, target->location.end, "Can't set variable"); + /* fallthrough */ + case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { + yp_global_variable_write_node_t *result = yp_global_variable_write_node_create(parser, &target->location, operator, value); + yp_node_destroy(parser, target); + + return (yp_node_t *) result; + } + case YP_NODE_LOCAL_VARIABLE_READ_NODE: { + yp_local_variable_read_node_t *local_read = (yp_local_variable_read_node_t *) target; + + yp_constant_id_t constant_id = local_read->constant_id; + uint32_t depth = local_read->depth; + + yp_location_t name_loc = target->location; + yp_node_destroy(parser, target); + + return (yp_node_t *) yp_local_variable_write_node_create(parser, constant_id, depth, value, &name_loc, operator); + } + case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { + yp_node_t *write_node = (yp_node_t *) yp_instance_variable_write_node_create(parser, (yp_instance_variable_read_node_t *) target, operator, value); + yp_node_destroy(parser, target); + return write_node; + } + case YP_NODE_MULTI_WRITE_NODE: { + yp_multi_write_node_t *multi_write = (yp_multi_write_node_t *) target; + yp_multi_write_node_operator_loc_set(multi_write, operator); + + if (value != NULL) { + multi_write->value = value; + multi_write->base.location.end = value->location.end; + } + + return (yp_node_t *) multi_write; + } + case YP_NODE_SPLAT_NODE: { + yp_splat_node_t *splat = (yp_splat_node_t *) target; + + if (splat->expression != NULL) { + splat->expression = parse_target(parser, splat->expression, operator, value); + } + + yp_location_t location = { .start = NULL, .end = NULL }; + yp_multi_write_node_t *multi_write = yp_multi_write_node_create(parser, operator, value, &location, &location); + yp_multi_write_node_targets_append(multi_write, (yp_node_t *) splat); + + return (yp_node_t *) multi_write; + } + case YP_NODE_CALL_NODE: { + yp_call_node_t *call = (yp_call_node_t *) target; + // If we have no arguments to the call node and we need this to be a + // target then this is either a method call or a local variable write. + if ( + (call->opening_loc.start == NULL) && + (call->arguments == NULL) && + (call->block == NULL) + ) { + if (call->receiver == NULL) { + // When we get here, we have a local variable write, because it + // was previously marked as a method call but now we have an =. + // This looks like: + // + // foo = 1 + // + // When it was parsed in the prefix position, foo was seen as a + // method call with no receiver and no arguments. Now we have an + // =, so we know it's a local variable write. + const yp_location_t message = call->message_loc; + + yp_parser_local_add_location(parser, message.start, message.end); + yp_node_destroy(parser, target); + + yp_constant_id_t constant_id = yp_parser_constant_id_location(parser, message.start, message.end); + target = (yp_node_t *) yp_local_variable_write_node_create(parser, constant_id, 0, value, &message, operator); + + if (token_is_numbered_parameter(message.start, message.end)) { + yp_diagnostic_list_append(&parser->error_list, message.start, message.end, "reserved for numbered parameter"); + } + + return target; + } + + // When we get here, we have a method call, because it was + // previously marked as a method call but now we have an =. This + // looks like: + // + // foo.bar = 1 + // + // When it was parsed in the prefix position, foo.bar was seen as a + // method call with no arguments. Now we have an =, so we know it's + // a method call with an argument. In this case we will create the + // arguments node, parse the argument, and add it to the list. + if (value) { + yp_arguments_node_t *arguments = yp_arguments_node_create(parser); + call->arguments = arguments; + yp_arguments_node_arguments_append(arguments, value); + target->location.end = arguments->base.location.end; + } + + // The method name needs to change. If we previously had foo, we now + // need foo=. In this case we'll allocate a new owned string, copy + // the previous method name in, and append an =. + size_t length = yp_string_length(&call->name); + + char *name = calloc(length + 2, sizeof(char)); + if (name == NULL) return NULL; + + snprintf(name, length + 2, "%.*s=", (int) length, yp_string_source(&call->name)); + + // Now switch the name to the new string. + yp_string_free(&call->name); + yp_string_owned_init(&call->name, name, length + 1); + + return target; + } + + // If there is no call operator and the message is "[]" then this is + // an aref expression, and we can transform it into an aset + // expression. + if ( + (call->operator_loc.start == NULL) && + (call->message_loc.start[0] == '[') && + (call->message_loc.end[-1] == ']') && + (call->block == NULL) + ) { + if (value != NULL) { + if (call->arguments == NULL) { + call->arguments = yp_arguments_node_create(parser); + } + + yp_arguments_node_arguments_append(call->arguments, value); + target->location.end = value->location.end; + } + + // Free the previous name and replace it with "[]=". + yp_string_free(&call->name); + yp_string_constant_init(&call->name, "[]=", 3); + return target; + } + + // If there are arguments on the call node, then it can't be a method + // call ending with = or a local variable write, so it must be a + // syntax error. In this case we'll fall through to our default + // handling. We need to free the value that we parsed because there + // is no way for us to attach it to the tree at this point. + if (value != NULL) { + yp_node_destroy(parser, value); + } + } + /* fallthrough */ + default: + // In this case we have a node that we don't know how to convert into a + // target. We need to treat it as an error. For now, we'll mark it as an + // error and just skip right past it. + yp_diagnostic_list_append(&parser->error_list, operator->start, operator->end, "Unexpected `='."); + return target; + } +} + +// Parse a list of targets for assignment. This is used in the case of a for +// loop or a multi-assignment. For example, in the following code: +// +// for foo, bar in baz +// ^^^^^^^^ +// +// The targets are `foo` and `bar`. This function will either return a single +// target node or a multi-target node. +static yp_node_t * +parse_targets(yp_parser_t *parser, yp_node_t *first_target, yp_binding_power_t binding_power) { + yp_token_t operator = not_provided(parser); + + // The first_target parameter can be NULL in the case that we're parsing a + // location that we know requires a multi write, as in the case of a for loop. + // In this case we will set up the parsing loop slightly differently. + if (first_target != NULL) { + first_target = parse_target(parser, first_target, &operator, NULL); + + if (!match_type_p(parser, YP_TOKEN_COMMA)) { + return first_target; + } + } + + yp_location_t lparen_loc = { .start = NULL, .end = NULL }; + yp_multi_write_node_t *result = yp_multi_write_node_create(parser, &operator, NULL, &lparen_loc, &lparen_loc); + + if (first_target != NULL) { + yp_multi_write_node_targets_append(result, first_target); + } + + bool has_splat = false; + + if (first_target == NULL || accept(parser, YP_TOKEN_COMMA)) { + do { + if (accept(parser, YP_TOKEN_USTAR)) { + // Here we have a splat operator. It can have a name or be anonymous. It + // can be the final target or be in the middle if there haven't been any + // others yet. + + if (has_splat) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Multiple splats in multi-assignment."); + } + + yp_token_t star_operator = parser->previous; + yp_node_t *name = NULL; + + if (token_begins_expression_p(parser->current.type)) { + yp_token_t operator = not_provided(parser); + name = parse_expression(parser, binding_power, "Expected an expression after '*'."); + name = parse_target(parser, name, &operator, NULL); + } + + yp_node_t *splat = (yp_node_t *) yp_splat_node_create(parser, &star_operator, name); + yp_multi_write_node_targets_append(result, splat); + has_splat = true; + } else if (accept(parser, YP_TOKEN_PARENTHESIS_LEFT)) { + // Here we have a parenthesized list of targets. We'll recurse down into + // the parentheses by calling parse_targets again and then finish out + // the node when it returns. + + yp_token_t lparen = parser->previous; + yp_node_t *first_child_target = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected an expression after '('."); + yp_node_t *child_target = parse_targets(parser, first_child_target, YP_BINDING_POWER_STATEMENT); + + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected an ')' after multi-assignment."); + yp_token_t rparen = parser->previous; + + if (child_target->type == YP_NODE_MULTI_WRITE_NODE && first_target == NULL && result->targets.size == 0) { + yp_node_destroy(parser, (yp_node_t *) result); + result = (yp_multi_write_node_t *) child_target; + result->base.location.start = lparen.start; + result->base.location.end = rparen.end; + result->lparen_loc = (yp_location_t) { .start = lparen.start, .end = lparen.end }; + result->rparen_loc = (yp_location_t) { .start = rparen.start, .end = rparen.end }; + } else { + yp_multi_write_node_t *target; + + if (child_target->type == YP_NODE_MULTI_WRITE_NODE) { + target = (yp_multi_write_node_t *) child_target; + target->lparen_loc = (yp_location_t) { .start = lparen.start, .end = lparen.end }; + target->rparen_loc = (yp_location_t) { .start = rparen.start, .end = rparen.end }; + } else { + yp_token_t operator = not_provided(parser); + + target = yp_multi_write_node_create( + parser, + &operator, + NULL, + &(yp_location_t) { .start = lparen.start, .end = lparen.end }, + &(yp_location_t) { .start = rparen.start, .end = rparen.end } + ); + + yp_multi_write_node_targets_append(target, child_target); + } + + target->base.location.end = rparen.end; + yp_multi_write_node_targets_append(result, (yp_node_t *) target); + } + } else { + if (!token_begins_expression_p(parser->current.type) && !match_type_p(parser, YP_TOKEN_USTAR)) { + if (first_target == NULL && result->targets.size == 0) { + // If we get here, then we weren't able to parse anything at all, so + // we need to return a missing node. + yp_node_destroy(parser, (yp_node_t *) result); + yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "Expected index after for."); + return (yp_node_t *) yp_missing_node_create(parser, operator.start, operator.end); + } + + // If we get here, then we have a trailing , in a multi write node. + // We need to indicate this somehow in the tree, so we'll add an + // anonymous splat. + yp_node_t *splat = (yp_node_t *) yp_splat_node_create(parser, &parser->previous, NULL); + yp_multi_write_node_targets_append(result, splat); + return (yp_node_t *) result; + } + + yp_node_t *target = parse_expression(parser, binding_power, "Expected another expression after ','."); + target = parse_target(parser, target, &operator, NULL); + + yp_multi_write_node_targets_append(result, target); + } + } while (accept(parser, YP_TOKEN_COMMA)); + } + + return (yp_node_t *) result; +} + +// Parse a list of statements separated by newlines or semicolons. +static yp_statements_node_t * +parse_statements(yp_parser_t *parser, yp_context_t context) { + // First, skip past any optional terminators that might be at the beginning of + // the statements. + while (accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE)); + + // If we have a terminator, then we can just return NULL. + if (context_terminator(context, &parser->current)) return NULL; + + yp_statements_node_t *statements = yp_statements_node_create(parser); + + // At this point we know we have at least one statement, and that it + // immediately follows the current token. + context_push(parser, context); + + while (true) { + yp_node_t *node = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected to be able to parse an expression."); + yp_statements_node_body_append(statements, node); + + // If we're recovering from a syntax error, then we need to stop parsing the + // statements now. + if (parser->recovering) { + // If this is the level of context where the recovery has happened, then + // we can mark the parser as done recovering. + if (context_terminator(context, &parser->current)) parser->recovering = false; + break; + } + + // If we have a terminator, then we will parse all consequtive terminators + // and then continue parsing the statements list. + if (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { + // If we have a terminator, then we will continue parsing the statements + // list. + while (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)); + if (context_terminator(context, &parser->current)) break; + + // Now we can continue parsing the list of statements. + continue; + } + + // At this point we have a list of statements that are not terminated by a + // newline or semicolon. At this point we need to check if we're at the end + // of the statements list. If we are, then we should break out of the loop. + if (context_terminator(context, &parser->current)) break; + + // At this point, we have a syntax error, because the statement was not + // terminated by a newline or semicolon, and we're not at the end of the + // statements list. Ideally we should scan forward to determine if we should + // insert a missing terminator or break out of parsing the statements list + // at this point. + // + // We don't have that yet, so instead we'll do a more naive approach. If we + // were unable to parse an expression, then we will skip past this token and + // continue parsing the statements list. Otherwise we'll add an error and + // continue parsing the statements list. + if (node->type == YP_NODE_MISSING_NODE) { + parser_lex(parser); + + while (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)); + if (context_terminator(context, &parser->current)) break; + } else { + expect(parser, YP_TOKEN_NEWLINE, "Expected a newline or semicolon after statement."); + } + } + + context_pop(parser); + return statements; +} + +// Parse all of the elements of a hash. +static void +parse_assocs(yp_parser_t *parser, yp_node_t *node) { + assert((node->type == YP_NODE_HASH_NODE) || (node->type == YP_NODE_KEYWORD_HASH_NODE)); + while (true) { + yp_node_t *element; + + switch (parser->current.type) { + case YP_TOKEN_USTAR_STAR: { + parser_lex(parser); + yp_token_t operator = parser->previous; + yp_node_t *value = NULL; + + if (token_begins_expression_p(parser->current.type)) { + value = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected an expression after ** in hash."); + } else if (yp_parser_local_depth(parser, &operator) == -1) { + yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "Expected an expression after ** in hash."); + } + + element = (yp_node_t *) yp_assoc_splat_node_create(parser, value, &operator); + break; + } + case YP_TOKEN_LABEL: { + parser_lex(parser); + + yp_node_t *key = (yp_node_t *) yp_symbol_node_label_create(parser, &parser->previous); + yp_token_t operator = not_provided(parser); + yp_node_t *value = NULL; + + if (token_begins_expression_p(parser->current.type)) { + value = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected an expression after the label in hash."); + } + + element = (yp_node_t *) yp_assoc_node_create(parser, key, &operator, value); + break; + } + default: { + yp_node_t *key = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a key in the hash literal."); + yp_token_t operator; + + if (yp_symbol_node_label_p(key)) { + operator = not_provided(parser); + } else { + expect(parser, YP_TOKEN_EQUAL_GREATER, "Expected a => between the key and the value in the hash."); + operator = parser->previous; + } + + yp_node_t *value = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value in the hash literal."); + element = (yp_node_t *) yp_assoc_node_create(parser, key, &operator, value); + break; + } + } + + if (node->type == YP_NODE_HASH_NODE) { + yp_hash_node_elements_append((yp_hash_node_t *) node, element); + } else { + yp_keyword_hash_node_elements_append((yp_keyword_hash_node_t *) node, element); + } + + // If there's no comma after the element, then we're done. + if (!accept(parser, YP_TOKEN_COMMA)) return; + + // If the next element starts with a label or a **, then we know we have + // another element in the hash, so we'll continue parsing. + if (match_any_type_p(parser, 2, YP_TOKEN_USTAR_STAR, YP_TOKEN_LABEL)) continue; + + // Otherwise we need to check if the subsequent token begins an expression. + // If it does, then we'll continue parsing. + if (token_begins_expression_p(parser->current.type)) continue; + + // Otherwise by default we will exit out of this loop. + return; + } +} + +// Parse a list of arguments. +static void +parse_arguments(yp_parser_t *parser, yp_arguments_node_t *arguments, bool accepts_forwarding, yp_token_type_t terminator) { + yp_binding_power_t binding_power = yp_binding_powers[parser->current.type].left; + + // First we need to check if the next token is one that could be the start of + // an argument. If it's not, then we can just return. + if ( + match_any_type_p(parser, 2, terminator, YP_TOKEN_EOF) || + (binding_power != YP_BINDING_POWER_UNSET && binding_power < YP_BINDING_POWER_RANGE) || + context_terminator(parser->current_context->context, &parser->current) + ) { + return; + } + + bool parsed_bare_hash = false; + bool parsed_block_argument = false; + + while (!match_type_p(parser, YP_TOKEN_EOF)) { + if (parsed_block_argument) { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unexpected argument after block argument."); + } + + yp_node_t *argument = NULL; + + switch (parser->current.type) { + case YP_TOKEN_USTAR_STAR: + case YP_TOKEN_LABEL: { + if (parsed_bare_hash) { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unexpected bare hash."); + } + + yp_keyword_hash_node_t *hash = yp_keyword_hash_node_create(parser); + argument = (yp_node_t *)hash; + + if (!match_any_type_p(parser, 7, terminator, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON, YP_TOKEN_EOF, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_KEYWORD_DO, YP_TOKEN_PARENTHESIS_RIGHT)) { + parse_assocs(parser, (yp_node_t *) hash); + } + + parsed_bare_hash = true; + break; + } + case YP_TOKEN_AMPERSAND: { + parser_lex(parser); + yp_token_t operator = parser->previous; + yp_node_t *expression = NULL; + + if (token_begins_expression_p(parser->current.type)) { + expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected to be able to parse an argument."); + } else if (yp_parser_local_depth(parser, &operator) == -1) { + yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "unexpected & when parent method is not forwarding."); + } + + argument = (yp_node_t *)yp_block_argument_node_create(parser, &operator, expression); + parsed_block_argument = true; + break; + } + case YP_TOKEN_USTAR: { + parser_lex(parser); + yp_token_t operator = parser->previous; + + if (match_any_type_p(parser, 2, YP_TOKEN_PARENTHESIS_RIGHT, YP_TOKEN_COMMA)) { + if (yp_parser_local_depth(parser, &parser->previous) == -1) { + yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "unexpected * when parent method is not forwarding."); + } + + argument = (yp_node_t *) yp_splat_node_create(parser, &operator, NULL); + } else { + yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected an expression after '*' in argument."); + + if (parsed_bare_hash) { + yp_diagnostic_list_append(&parser->error_list, operator.start, expression->location.end, "Unexpected splat argument after double splat."); + } + + argument = (yp_node_t *) yp_splat_node_create(parser, &operator, expression); + } + + break; + } + case YP_TOKEN_UDOT_DOT_DOT: { + if (accepts_forwarding) { + parser_lex(parser); + + if (token_begins_expression_p(parser->current.type)) { + // If the token begins an expression then this ... was not actually + // argument forwarding but was instead a range. + yp_token_t operator = parser->previous; + yp_node_t *right = parse_expression(parser, YP_BINDING_POWER_RANGE, "Expected a value after the operator."); + argument = (yp_node_t *) yp_range_node_create(parser, NULL, &operator, right); + } else { + if (yp_parser_local_depth(parser, &parser->previous) == -1) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "unexpected ... when parent method is not forwarding."); + } + + argument = (yp_node_t *)yp_forwarding_arguments_node_create(parser, &parser->previous); + break; + } + } + } + /* fallthrough */ + default: { + if (argument == NULL) { + argument = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected to be able to parse an argument."); + } + + if (yp_symbol_node_label_p(argument) || accept(parser, YP_TOKEN_EQUAL_GREATER)) { + if (parsed_bare_hash) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected bare hash argument."); + } + + yp_token_t operator; + if (parser->previous.type == YP_TOKEN_EQUAL_GREATER) { + operator = parser->previous; + } else { + operator = not_provided(parser); + } + + yp_keyword_hash_node_t *bare_hash = yp_keyword_hash_node_create(parser); + + // Finish parsing the one we are part way through + yp_node_t *value = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value in the hash literal."); + + argument = (yp_node_t *) yp_assoc_node_create(parser, argument, &operator, value); + yp_keyword_hash_node_elements_append(bare_hash, argument); + argument = (yp_node_t *) bare_hash; + + // Then parse more if we have a comma + if (accept(parser, YP_TOKEN_COMMA) && ( + token_begins_expression_p(parser->current.type) || + match_any_type_p(parser, 2, YP_TOKEN_USTAR_STAR, YP_TOKEN_LABEL) + )) { + parse_assocs(parser, (yp_node_t *) bare_hash); + } + + parsed_bare_hash = true; + } + + break; + } + } + + yp_arguments_node_arguments_append(arguments, argument); + + // If parsing the argument failed, we need to stop parsing arguments. + if (argument->type == YP_NODE_MISSING_NODE || parser->recovering) break; + + // If the terminator of these arguments is not EOF, then we have a specific + // token we're looking for. In that case we can accept a newline here + // because it is not functioning as a statement terminator. + if (terminator != YP_TOKEN_EOF) accept(parser, YP_TOKEN_NEWLINE); + + if (parser->previous.type == YP_TOKEN_COMMA && parsed_bare_hash) { + // If we previously were on a comma and we just parsed a bare hash, then + // we want to continue parsing arguments. This is because the comma was + // grabbed up by the hash parser. + } else { + // If there is no comma at the end of the argument list then we're done + // parsing arguments and can break out of this loop. + if (!accept(parser, YP_TOKEN_COMMA)) break; + } + + // If we hit the terminator, then that means we have a trailing comma so we + // can accept that output as well. + if (match_type_p(parser, terminator)) break; + } +} + +// Required parameters on method, block, and lambda declarations can be +// destructured using parentheses. This looks like: +// +// def foo((bar, baz)) +// end +// +// It can recurse infinitely down, and splats are allowed to group arguments. +static yp_required_destructured_parameter_node_t * +parse_required_destructured_parameter(yp_parser_t *parser) { + expect(parser, YP_TOKEN_PARENTHESIS_LEFT, "Expected '(' to start a required parameter."); + + yp_token_t opening = parser->previous; + yp_required_destructured_parameter_node_t *node = yp_required_destructured_parameter_node_create(parser, &opening); + bool parsed_splat = false; + + do { + yp_node_t *param; + + if (node->parameters.size > 0 && match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { + if (parsed_splat) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected splat after splat."); + } + + param = (yp_node_t *) yp_splat_node_create(parser, &parser->previous, NULL); + yp_required_destructured_parameter_node_append_parameter(node, param); + break; + } + + if (match_type_p(parser, YP_TOKEN_PARENTHESIS_LEFT)) { + param = (yp_node_t *) parse_required_destructured_parameter(parser); + } else if (accept(parser, YP_TOKEN_USTAR)) { + if (parsed_splat) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected splat after splat."); + } + + yp_token_t star = parser->previous; + yp_node_t *value = NULL; + + if (accept(parser, YP_TOKEN_IDENTIFIER)) { + yp_token_t name = parser->previous; + value = (yp_node_t *) yp_required_parameter_node_create(parser, &name); + yp_parser_local_add_token(parser, &name); + } + + param = (yp_node_t *) yp_splat_node_create(parser, &star, value); + parsed_splat = true; + } else { + expect(parser, YP_TOKEN_IDENTIFIER, "Expected an identifier for a required parameter."); + yp_token_t name = parser->previous; + + param = (yp_node_t *) yp_required_parameter_node_create(parser, &name); + yp_parser_local_add_token(parser, &name); + } + + yp_required_destructured_parameter_node_append_parameter(node, param); + } while (accept(parser, YP_TOKEN_COMMA)); + + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' to end a required parameter."); + yp_required_destructured_parameter_node_closing_set(node, &parser->previous); + + return node; +} + +// This represents the different order states we can be in when parsing +// method parameters. +typedef enum { + YP_PARAMETERS_NO_CHANGE = 0, // Extra state for tokens that should not change the state + YP_PARAMETERS_ORDER_NOTHING_AFTER = 1, + YP_PARAMETERS_ORDER_KEYWORDS_REST, + YP_PARAMETERS_ORDER_KEYWORDS, + YP_PARAMETERS_ORDER_REST, + YP_PARAMETERS_ORDER_AFTER_OPTIONAL, + YP_PARAMETERS_ORDER_OPTIONAL, + YP_PARAMETERS_ORDER_NAMED, + YP_PARAMETERS_ORDER_NONE, + +} yp_parameters_order_t; + +// This matches parameters tokens with parameters state. +static yp_parameters_order_t parameters_ordering[YP_TOKEN_MAXIMUM] = { + [0] = YP_PARAMETERS_NO_CHANGE, + [YP_TOKEN_AMPERSAND] = YP_PARAMETERS_ORDER_NOTHING_AFTER, + [YP_TOKEN_UDOT_DOT_DOT] = YP_PARAMETERS_ORDER_NOTHING_AFTER, + [YP_TOKEN_IDENTIFIER] = YP_PARAMETERS_ORDER_NAMED, + [YP_TOKEN_PARENTHESIS_LEFT] = YP_PARAMETERS_ORDER_NAMED, + [YP_TOKEN_EQUAL] = YP_PARAMETERS_ORDER_OPTIONAL, + [YP_TOKEN_LABEL] = YP_PARAMETERS_ORDER_KEYWORDS, + [YP_TOKEN_USTAR] = YP_PARAMETERS_ORDER_AFTER_OPTIONAL, + [YP_TOKEN_STAR] = YP_PARAMETERS_ORDER_AFTER_OPTIONAL, + [YP_TOKEN_USTAR_STAR] = YP_PARAMETERS_ORDER_KEYWORDS_REST, + [YP_TOKEN_STAR_STAR] = YP_PARAMETERS_ORDER_KEYWORDS_REST +}; + +// Check if current parameter follows valid parameters ordering. If not it adds an +// error to the list without stopping the parsing, otherwise sets the parameters state +// to the one corresponding to the current parameter. +static void +update_parameter_state(yp_parser_t *parser, yp_token_t *token, yp_parameters_order_t *current) { + yp_parameters_order_t state = parameters_ordering[token->type]; + if (state == YP_PARAMETERS_NO_CHANGE) return; + + // If we see another ordered argument after a optional argument + // we only continue parsing ordered arguments until we stop seeing ordered arguments + if (*current == YP_PARAMETERS_ORDER_OPTIONAL && state == YP_PARAMETERS_ORDER_NAMED) { + *current = YP_PARAMETERS_ORDER_AFTER_OPTIONAL; + return; + } else if (*current == YP_PARAMETERS_ORDER_AFTER_OPTIONAL && state == YP_PARAMETERS_ORDER_NAMED) { + return; + } + + if (*current == YP_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) { + // We know what transition we failed on, so we can provide a better error here. + yp_diagnostic_list_append(&parser->error_list, token->start, token->end, "Unexpected parameter order"); + } else if (state < *current) { + *current = state; + } +} + +// Parse a list of parameters on a method definition. +static yp_parameters_node_t * +parse_parameters( + yp_parser_t *parser, + yp_binding_power_t binding_power, + bool uses_parentheses, + bool allows_trailing_comma, + bool allows_forwarding_parameter +) { + yp_parameters_node_t *params = yp_parameters_node_create(parser); + bool looping = true; + + yp_do_loop_stack_push(parser, false); + + yp_parameters_order_t order = YP_PARAMETERS_ORDER_NONE; + + do { + switch (parser->current.type) { + case YP_TOKEN_PARENTHESIS_LEFT: { + update_parameter_state(parser, &parser->current, &order); + yp_node_t *param = (yp_node_t *) parse_required_destructured_parameter(parser); + + if (order > YP_PARAMETERS_ORDER_AFTER_OPTIONAL) { + yp_parameters_node_requireds_append(params, param); + } else { + yp_parameters_node_posts_append(params, param); + } + break; + } + case YP_TOKEN_AMPERSAND: { + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + yp_token_t operator = parser->previous; + yp_token_t name; + + if (accept(parser, YP_TOKEN_IDENTIFIER)) { + name = parser->previous; + yp_parser_parameter_name_check(parser, &name); + yp_parser_local_add_token(parser, &name); + } else { + name = not_provided(parser); + yp_parser_local_add_token(parser, &operator); + } + + yp_block_parameter_node_t *param = yp_block_parameter_node_create(parser, &name, &operator); + yp_parameters_node_block_set(params, param); + break; + } + case YP_TOKEN_UDOT_DOT_DOT: { + if (!allows_forwarding_parameter) { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unexpected ..."); + } + if (order > YP_PARAMETERS_ORDER_NOTHING_AFTER) { + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + yp_parser_local_add_token(parser, &parser->previous); + yp_forwarding_parameter_node_t *param = yp_forwarding_parameter_node_create(parser, &parser->previous); + yp_parameters_node_keyword_rest_set(params, (yp_node_t *)param); + } else { + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + } + break; + } + case YP_TOKEN_IDENTIFIER: { + parser_lex(parser); + + if (parser->current.type == YP_TOKEN_EQUAL) { + update_parameter_state(parser, &parser->current, &order); + } else { + update_parameter_state(parser, &parser->previous, &order); + } + + yp_token_t name = parser->previous; + yp_parser_parameter_name_check(parser, &name); + yp_parser_local_add_token(parser, &name); + + if (accept(parser, YP_TOKEN_EQUAL)) { + yp_token_t operator = parser->previous; + context_push(parser, YP_CONTEXT_DEFAULT_PARAMS); + yp_node_t *value = parse_expression(parser, binding_power, "Expected to find a default value for the parameter."); + + yp_optional_parameter_node_t *param = yp_optional_parameter_node_create(parser, &name, &operator, value); + yp_parameters_node_optionals_append(params, param); + context_pop(parser); + + // If parsing the value of the parameter resulted in error recovery, + // then we can put a missing node in its place and stop parsing the + // parameters entirely now. + if (parser->recovering) { + looping = false; + break; + } + } else if (order > YP_PARAMETERS_ORDER_AFTER_OPTIONAL) { + yp_required_parameter_node_t *param = yp_required_parameter_node_create(parser, &name); + yp_parameters_node_requireds_append(params, (yp_node_t *) param); + } else { + yp_required_parameter_node_t *param = yp_required_parameter_node_create(parser, &name); + yp_parameters_node_posts_append(params, (yp_node_t *) param); + } + + break; + } + case YP_TOKEN_LABEL: { + if (!uses_parentheses) parser->in_keyword_arg = true; + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + yp_token_t name = parser->previous; + yp_token_t local = name; + local.end -= 1; + + yp_parser_parameter_name_check(parser, &local); + yp_parser_local_add_token(parser, &local); + + switch (parser->current.type) { + case YP_TOKEN_COMMA: + case YP_TOKEN_PARENTHESIS_RIGHT: + case YP_TOKEN_PIPE: { + yp_node_t *param = (yp_node_t *) yp_keyword_parameter_node_create(parser, &name, NULL); + yp_parameters_node_keywords_append(params, param); + break; + } + case YP_TOKEN_SEMICOLON: + case YP_TOKEN_NEWLINE: { + if (uses_parentheses) { + looping = false; + break; + } + + yp_node_t *param = (yp_node_t *) yp_keyword_parameter_node_create(parser, &name, NULL); + yp_parameters_node_keywords_append(params, param); + break; + } + default: { + yp_node_t *value = NULL; + if (token_begins_expression_p(parser->current.type)) { + context_push(parser, YP_CONTEXT_DEFAULT_PARAMS); + value = parse_expression(parser, binding_power, "Expected to find a default value for the keyword parameter."); + context_pop(parser); + } + + yp_node_t *param = (yp_node_t *) yp_keyword_parameter_node_create(parser, &name, value); + yp_parameters_node_keywords_append(params, param); + + // If parsing the value of the parameter resulted in error recovery, + // then we can put a missing node in its place and stop parsing the + // parameters entirely now. + if (parser->recovering) { + looping = false; + break; + } + } + } + + parser->in_keyword_arg = false; + break; + } + case YP_TOKEN_USTAR: + case YP_TOKEN_STAR: { + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + yp_token_t operator = parser->previous; + yp_token_t name; + + if (accept(parser, YP_TOKEN_IDENTIFIER)) { + name = parser->previous; + yp_parser_parameter_name_check(parser, &name); + yp_parser_local_add_token(parser, &name); + } else { + name = not_provided(parser); + yp_parser_local_add_token(parser, &operator); + } + + yp_rest_parameter_node_t *param = yp_rest_parameter_node_create(parser, &operator, &name); + yp_parameters_node_rest_set(params, param); + break; + } + case YP_TOKEN_STAR_STAR: + case YP_TOKEN_USTAR_STAR: { + update_parameter_state(parser, &parser->current, &order); + parser_lex(parser); + + yp_token_t operator = parser->previous; + yp_node_t *param; + + if (accept(parser, YP_TOKEN_KEYWORD_NIL)) { + param = (yp_node_t *) yp_no_keywords_parameter_node_create(parser, &operator, &parser->previous); + } else { + yp_token_t name; + + if (accept(parser, YP_TOKEN_IDENTIFIER)) { + name = parser->previous; + yp_parser_parameter_name_check(parser, &name); + yp_parser_local_add_token(parser, &name); + } else { + name = not_provided(parser); + yp_parser_local_add_token(parser, &operator); + } + + param = (yp_node_t *) yp_keyword_rest_parameter_node_create(parser, &operator, &name); + } + + yp_parameters_node_keyword_rest_set(params, param); + break; + } + case YP_TOKEN_CONSTANT: + parser_lex(parser); + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Formal argument cannot be a constant"); + break; + case YP_TOKEN_INSTANCE_VARIABLE: + parser_lex(parser); + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Formal argument cannot be an instance variable"); + break; + case YP_TOKEN_GLOBAL_VARIABLE: + parser_lex(parser); + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Formal argument cannot be a global variable"); + break; + case YP_TOKEN_CLASS_VARIABLE: + parser_lex(parser); + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Formal argument cannot be a class variable"); + break; + default: + if (parser->previous.type == YP_TOKEN_COMMA) { + if (allows_trailing_comma) { + // If we get here, then we have a trailing comma in a block + // parameter list. We need to create an anonymous rest parameter to + // represent it. + yp_token_t name = not_provided(parser); + yp_rest_parameter_node_t *param = yp_rest_parameter_node_create(parser, &parser->previous, &name); + yp_parameters_node_rest_set(params, param); + } else { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected ','."); + } + } + + looping = false; + break; + } + + if (looping && uses_parentheses) { + accept(parser, YP_TOKEN_NEWLINE); + } + } while (looping && accept(parser, YP_TOKEN_COMMA)); + + yp_do_loop_stack_pop(parser); + return params; +} + +// Parse any number of rescue clauses. This will form a linked list of if +// nodes pointing to each other from the top. +static inline void +parse_rescues(yp_parser_t *parser, yp_begin_node_t *parent_node) { + yp_rescue_node_t *current = NULL; + + while (accept(parser, YP_TOKEN_KEYWORD_RESCUE)) { + yp_rescue_node_t *rescue = yp_rescue_node_create(parser, &parser->previous); + + switch (parser->current.type) { + case YP_TOKEN_EQUAL_GREATER: { + // Here we have an immediate => after the rescue keyword, in which case + // we're going to have an empty list of exceptions to rescue (which + // implies StandardError). + parser_lex(parser); + yp_rescue_node_operator_set(rescue, &parser->previous); + + yp_node_t *node = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected an exception variable after `=>` in rescue statement."); + yp_token_t operator = not_provided(parser); + node = parse_target(parser, node, &operator, NULL); + + rescue->exception = node; + break; + } + case YP_TOKEN_NEWLINE: + case YP_TOKEN_SEMICOLON: + case YP_TOKEN_KEYWORD_THEN: + // Here we have a terminator for the rescue keyword, in which case we're + // going to just continue on. + break; + default: { + if (token_begins_expression_p(parser->current.type) || match_type_p(parser, YP_TOKEN_USTAR)) { + // Here we have something that could be an exception expression, so + // we'll attempt to parse it here and any others delimited by commas. + + do { + yp_node_t *expression = parse_starred_expression(parser, YP_BINDING_POWER_DEFINED, "Expected to find a rescued expression."); + yp_rescue_node_exceptions_append(rescue, expression); + + // If we hit a newline, then this is the end of the rescue expression. We + // can continue on to parse the statements. + if (match_any_type_p(parser, 3, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON, YP_TOKEN_KEYWORD_THEN)) break; + + // If we hit a `=>` then we're going to parse the exception variable. Once + // we've done that, we'll break out of the loop and parse the statements. + if (accept(parser, YP_TOKEN_EQUAL_GREATER)) { + yp_rescue_node_operator_set(rescue, &parser->previous); + + yp_node_t *node = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected an exception variable after `=>` in rescue statement."); + yp_token_t operator = not_provided(parser); + node = parse_target(parser, node, &operator, NULL); + + yp_rescue_node_exception_set(rescue, node); + break; + } + } while (accept(parser, YP_TOKEN_COMMA)); + } + } + } + + if (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { + accept(parser, YP_TOKEN_KEYWORD_THEN); + } else { + expect(parser, YP_TOKEN_KEYWORD_THEN, "Expected a terminator after rescue clause."); + } + + if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { + yp_statements_node_t *statements = parse_statements(parser, YP_CONTEXT_RESCUE); + if (statements) { + yp_rescue_node_statements_set(rescue, statements); + } + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + } + + if (current == NULL) { + yp_begin_node_rescue_clause_set(parent_node, rescue); + } else { + yp_rescue_node_consequent_set(current, rescue); + } + + current = rescue; + } + + // The end node locations on rescue nodes will not be set correctly + // since we won't know the end until we've found all consequent + // clauses. This sets the end location on all rescues once we know it + if (current) { + const char *end_to_set = current->base.location.end; + current = parent_node->rescue_clause; + while (current) { + current->base.location.end = end_to_set; + current = current->consequent; + } + } + + if (accept(parser, YP_TOKEN_KEYWORD_ELSE)) { + yp_token_t else_keyword = parser->previous; + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + + yp_statements_node_t *else_statements = NULL; + if (!match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_END, YP_TOKEN_KEYWORD_ENSURE)) { + else_statements = parse_statements(parser, YP_CONTEXT_RESCUE_ELSE); + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + } + + yp_else_node_t *else_clause = yp_else_node_create(parser, &else_keyword, else_statements, &parser->current); + yp_begin_node_else_clause_set(parent_node, else_clause); + } + + if (accept(parser, YP_TOKEN_KEYWORD_ENSURE)) { + yp_token_t ensure_keyword = parser->previous; + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + + yp_statements_node_t *ensure_statements = NULL; + if (!match_type_p(parser, YP_TOKEN_KEYWORD_END)) { + ensure_statements = parse_statements(parser, YP_CONTEXT_ENSURE); + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + } + + yp_ensure_node_t *ensure_clause = yp_ensure_node_create(parser, &ensure_keyword, ensure_statements, &parser->current); + yp_begin_node_ensure_clause_set(parent_node, ensure_clause); + } + + if (parser->current.type == YP_TOKEN_KEYWORD_END) { + yp_begin_node_end_keyword_set(parent_node, &parser->current); + } else { + yp_token_t end_keyword = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + yp_begin_node_end_keyword_set(parent_node, &end_keyword); + } +} + +static inline yp_begin_node_t * +parse_rescues_as_begin(yp_parser_t *parser, yp_statements_node_t *statements) { + yp_token_t no_begin_token = not_provided(parser); + yp_begin_node_t *begin_node = yp_begin_node_create(parser, &no_begin_token, statements); + parse_rescues(parser, begin_node); + + // All nodes within a begin node are optional, so we look + // for the earliest possible node that we can use to set + // the BeginNode's start location + const char * start = begin_node->base.location.start; + if (begin_node->statements) { + start = begin_node->statements->base.location.start; + } else if (begin_node->rescue_clause) { + start = begin_node->rescue_clause->base.location.start; + } else if (begin_node->else_clause) { + start = begin_node->else_clause->base.location.start; + } else if (begin_node->ensure_clause) { + start = begin_node->ensure_clause->base.location.start; + } + + begin_node->base.location.start = start; + return begin_node; +} + +// Parse a list of parameters and local on a block definition. +static yp_block_parameters_node_t * +parse_block_parameters( + yp_parser_t *parser, + bool allows_trailing_comma, + const yp_token_t *opening, + bool is_lambda_literal +) { + yp_parameters_node_t *parameters = NULL; + if (!match_type_p(parser, YP_TOKEN_SEMICOLON)) { + parameters = parse_parameters( + parser, + is_lambda_literal ? YP_BINDING_POWER_DEFINED : YP_BINDING_POWER_INDEX, + false, + allows_trailing_comma, + false + ); + } + + yp_block_parameters_node_t *block_parameters = yp_block_parameters_node_create(parser, parameters, opening); + if (accept(parser, YP_TOKEN_SEMICOLON)) { + do { + expect(parser, YP_TOKEN_IDENTIFIER, "Expected a local variable name."); + yp_parser_local_add_token(parser, &parser->previous); + yp_block_parameters_node_append_local(block_parameters, &parser->previous); + } while (accept(parser, YP_TOKEN_COMMA)); + } + + return block_parameters; +} + +// Parse a block. +static yp_block_node_t * +parse_block(yp_parser_t *parser) { + yp_token_t opening = parser->previous; + accept(parser, YP_TOKEN_NEWLINE); + + yp_accepts_block_stack_push(parser, true); + yp_parser_scope_push(parser, false); + yp_block_parameters_node_t *parameters = NULL; + + if (accept(parser, YP_TOKEN_PIPE)) { + yp_token_t block_parameters_opening = parser->previous; + + if (match_type_p(parser, YP_TOKEN_PIPE)) { + parameters = yp_block_parameters_node_create(parser, NULL, &block_parameters_opening); + parser->command_start = true; + parser_lex(parser); + } else { + parameters = parse_block_parameters(parser, true, &block_parameters_opening, false); + accept(parser, YP_TOKEN_NEWLINE); + parser->command_start = true; + expect(parser, YP_TOKEN_PIPE, "Expected block parameters to end with '|'."); + } + + yp_block_parameters_node_closing_set(parameters, &parser->previous); + } + + accept(parser, YP_TOKEN_NEWLINE); + yp_node_t *statements = NULL; + + if (opening.type == YP_TOKEN_BRACE_LEFT) { + if (!match_type_p(parser, YP_TOKEN_BRACE_RIGHT)) { + statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_BLOCK_BRACES); + } + + expect(parser, YP_TOKEN_BRACE_RIGHT, "Expected block beginning with '{' to end with '}'."); + } else { + if (!match_type_p(parser, YP_TOKEN_KEYWORD_END)) { + if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_KEYWORD_ENSURE)) { + statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_BLOCK_KEYWORDS); + } + + if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { + assert(statements == NULL || statements->type == YP_NODE_STATEMENTS_NODE); + statements = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) statements); + } + } + + expect(parser, YP_TOKEN_KEYWORD_END, "Expected block beginning with 'do' to end with 'end'."); + } + + yp_constant_id_list_t locals = parser->current_scope->locals; + yp_parser_scope_pop(parser); + yp_accepts_block_stack_pop(parser); + return yp_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous); +} + +// Parse a list of arguments and their surrounding parentheses if they are +// present. +static void +parse_arguments_list(yp_parser_t *parser, yp_arguments_t *arguments, bool accepts_block) { + if (accept(parser, YP_TOKEN_PARENTHESIS_LEFT)) { + arguments->opening_loc = ((yp_location_t) { .start = parser->previous.start, .end = parser->previous.end }); + + if (accept(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { + arguments->closing_loc = ((yp_location_t) { .start = parser->previous.start, .end = parser->previous.end }); + } else { + arguments->arguments = yp_arguments_node_create(parser); + + yp_accepts_block_stack_push(parser, true); + parse_arguments(parser, arguments->arguments, true, YP_TOKEN_PARENTHESIS_RIGHT); + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a ')' to close the argument list."); + yp_accepts_block_stack_pop(parser); + + arguments->closing_loc = ((yp_location_t) { .start = parser->previous.start, .end = parser->previous.end }); + } + } else if ((token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR)) && !match_type_p(parser, YP_TOKEN_BRACE_LEFT)) { + yp_accepts_block_stack_push(parser, false); + + // If we get here, then the subsequent token cannot be used as an infix + // operator. In this case we assume the subsequent token is part of an + // argument to this method call. + arguments->arguments = yp_arguments_node_create(parser); + parse_arguments(parser, arguments->arguments, true, YP_TOKEN_EOF); + + yp_accepts_block_stack_pop(parser); + } + + // If we're at the end of the arguments, we can now check if there is a block + // node that starts with a {. If there is, then we can parse it and add it to + // the arguments. + if (accepts_block) { + if (accept(parser, YP_TOKEN_BRACE_LEFT)) { + arguments->block = parse_block(parser); + } else if (yp_accepts_block_stack_p(parser) && accept(parser, YP_TOKEN_KEYWORD_DO)) { + arguments->block = parse_block(parser); + } + } +} + +static inline yp_node_t * +parse_conditional(yp_parser_t *parser, yp_context_t context) { + yp_token_t keyword = parser->previous; + + context_push(parser, YP_CONTEXT_PREDICATE); + yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_MODIFIER, "Expected to find a predicate for the conditional."); + + // Predicates are closed by a term, a "then", or a term and then a "then". + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + accept(parser, YP_TOKEN_KEYWORD_THEN); + + context_pop(parser); + yp_statements_node_t *statements = NULL; + + if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_ELSIF, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_KEYWORD_END)) { + yp_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, context); + yp_accepts_block_stack_pop(parser); + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + } + + yp_token_t end_keyword = not_provided(parser); + yp_node_t *parent; + + switch (context) { + case YP_CONTEXT_IF: + parent = (yp_node_t *) yp_if_node_create(parser, &keyword, predicate, statements, NULL, &end_keyword); + break; + case YP_CONTEXT_UNLESS: + parent = (yp_node_t *) yp_unless_node_create(parser, &keyword, predicate, statements); + break; + default: + parent = NULL; + assert(false && "unreachable"); + break; + } + + yp_node_t *current = parent; + + // Parse any number of elsif clauses. This will form a linked list of if + // nodes pointing to each other from the top. + if (context == YP_CONTEXT_IF) { + while (accept(parser, YP_TOKEN_KEYWORD_ELSIF)) { + yp_token_t elsif_keyword = parser->previous; + yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_MODIFIER, "Expected to find a predicate for the elsif clause."); + + // Predicates are closed by a term, a "then", or a term and then a "then". + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + accept(parser, YP_TOKEN_KEYWORD_THEN); + + yp_accepts_block_stack_push(parser, true); + yp_statements_node_t *statements = parse_statements(parser, YP_CONTEXT_ELSIF); + yp_accepts_block_stack_pop(parser); + + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + + yp_node_t *elsif = (yp_node_t *) yp_if_node_create(parser, &elsif_keyword, predicate, statements, NULL, &end_keyword); + ((yp_if_node_t *) current)->consequent = elsif; + current = elsif; + } + } + + if (match_type_p(parser, YP_TOKEN_KEYWORD_ELSE)) { + parser_lex(parser); + yp_token_t else_keyword = parser->previous; + + yp_accepts_block_stack_push(parser, true); + yp_statements_node_t *else_statements = parse_statements(parser, YP_CONTEXT_ELSE); + yp_accepts_block_stack_pop(parser); + + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `else` clause."); + + yp_else_node_t *else_node = yp_else_node_create(parser, &else_keyword, else_statements, &parser->previous); + + switch (context) { + case YP_CONTEXT_IF: + ((yp_if_node_t *) current)->consequent = (yp_node_t *) else_node; + // Recurse down if nodes setting the appropriate end location in all cases + yp_node_t * recursing_node = parent; + bool recursing = true; + + while (recursing) { + switch (recursing_node->type) { + case YP_NODE_IF_NODE: + yp_if_node_end_keyword_loc_set((yp_if_node_t *) recursing_node, &parser->previous); + recursing_node = ((yp_if_node_t *) recursing_node)->consequent; + break; + case YP_NODE_ELSE_NODE: + yp_else_node_end_keyword_loc_set((yp_else_node_t *) recursing_node, &parser->previous); + recursing = false; + break; + default: { + recursing = false; + break; + } + } + } + break; + case YP_CONTEXT_UNLESS: + ((yp_unless_node_t *) parent)->consequent = else_node; + yp_unless_node_end_keyword_loc_set((yp_unless_node_t *) parent, &parser->previous); + break; + default: + assert(false && "unreachable"); + break; + } + } else { + expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `if` statement."); + + switch (context) { + case YP_CONTEXT_IF: + yp_if_node_end_keyword_loc_set((yp_if_node_t *) parent, &parser->previous); + break; + case YP_CONTEXT_UNLESS: + yp_unless_node_end_keyword_loc_set((yp_unless_node_t *) parent, &parser->previous); + break; + default: + assert(false && "unreachable"); + break; + } + } + + return parent; +} + +// This macro allows you to define a case statement for all of the keywords. +// It's meant to be used in a switch statement. +#define YP_CASE_KEYWORD YP_TOKEN_KEYWORD___ENCODING__: case YP_TOKEN_KEYWORD___FILE__: case YP_TOKEN_KEYWORD___LINE__: \ + case YP_TOKEN_KEYWORD_ALIAS: case YP_TOKEN_KEYWORD_AND: case YP_TOKEN_KEYWORD_BEGIN: case YP_TOKEN_KEYWORD_BEGIN_UPCASE: \ + case YP_TOKEN_KEYWORD_BREAK: case YP_TOKEN_KEYWORD_CASE: case YP_TOKEN_KEYWORD_CLASS: case YP_TOKEN_KEYWORD_DEF: \ + case YP_TOKEN_KEYWORD_DEFINED: case YP_TOKEN_KEYWORD_DO: case YP_TOKEN_KEYWORD_DO_LOOP: case YP_TOKEN_KEYWORD_ELSE: \ + case YP_TOKEN_KEYWORD_ELSIF: case YP_TOKEN_KEYWORD_END: case YP_TOKEN_KEYWORD_END_UPCASE: case YP_TOKEN_KEYWORD_ENSURE: \ + case YP_TOKEN_KEYWORD_FALSE: case YP_TOKEN_KEYWORD_FOR: case YP_TOKEN_KEYWORD_IF: case YP_TOKEN_KEYWORD_IN: \ + case YP_TOKEN_KEYWORD_MODULE: case YP_TOKEN_KEYWORD_NEXT: case YP_TOKEN_KEYWORD_NIL: case YP_TOKEN_KEYWORD_NOT: \ + case YP_TOKEN_KEYWORD_OR: case YP_TOKEN_KEYWORD_REDO: case YP_TOKEN_KEYWORD_RESCUE: case YP_TOKEN_KEYWORD_RETRY: \ + case YP_TOKEN_KEYWORD_RETURN: case YP_TOKEN_KEYWORD_SELF: case YP_TOKEN_KEYWORD_SUPER: case YP_TOKEN_KEYWORD_THEN: \ + case YP_TOKEN_KEYWORD_TRUE: case YP_TOKEN_KEYWORD_UNDEF: case YP_TOKEN_KEYWORD_UNLESS: case YP_TOKEN_KEYWORD_UNTIL: \ + case YP_TOKEN_KEYWORD_WHEN: case YP_TOKEN_KEYWORD_WHILE: case YP_TOKEN_KEYWORD_YIELD + + +// This macro allows you to define a case statement for all of the operators. +// It's meant to be used in a switch statement. +#define YP_CASE_OPERATOR YP_TOKEN_AMPERSAND: case YP_TOKEN_BACKTICK: case YP_TOKEN_BANG_EQUAL: \ + case YP_TOKEN_BANG_TILDE: case YP_TOKEN_BANG: case YP_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: \ + case YP_TOKEN_BRACKET_LEFT_RIGHT: case YP_TOKEN_CARET: case YP_TOKEN_EQUAL_EQUAL_EQUAL: case YP_TOKEN_EQUAL_EQUAL: \ + case YP_TOKEN_EQUAL_TILDE: case YP_TOKEN_GREATER_EQUAL: case YP_TOKEN_GREATER_GREATER: case YP_TOKEN_GREATER: \ + case YP_TOKEN_LESS_EQUAL_GREATER: case YP_TOKEN_LESS_EQUAL: case YP_TOKEN_LESS_LESS: case YP_TOKEN_LESS: \ + case YP_TOKEN_MINUS: case YP_TOKEN_PERCENT: case YP_TOKEN_PIPE: case YP_TOKEN_PLUS: case YP_TOKEN_SLASH: \ + case YP_TOKEN_STAR_STAR: case YP_TOKEN_STAR: case YP_TOKEN_TILDE: case YP_TOKEN_UMINUS: case YP_TOKEN_UMINUS_NUM: \ + case YP_TOKEN_UPLUS: case YP_TOKEN_USTAR: case YP_TOKEN_USTAR_STAR + +// This macro allows you to define a case statement for all of the token types +// that represent the beginning of nodes that are "primitives" in a pattern +// matching expression. +#define YP_CASE_PRIMITIVE YP_TOKEN_INTEGER: case YP_TOKEN_FLOAT: case YP_TOKEN_RATIONAL_NUMBER: \ + case YP_TOKEN_IMAGINARY_NUMBER: case YP_TOKEN_SYMBOL_BEGIN: case YP_TOKEN_REGEXP_BEGIN: case YP_TOKEN_BACKTICK: \ + case YP_TOKEN_PERCENT_LOWER_X: case YP_TOKEN_PERCENT_LOWER_I: case YP_TOKEN_PERCENT_LOWER_W: \ + case YP_TOKEN_PERCENT_UPPER_I: case YP_TOKEN_PERCENT_UPPER_W: case YP_TOKEN_STRING_BEGIN: case YP_TOKEN_KEYWORD_NIL: \ + case YP_TOKEN_KEYWORD_SELF: case YP_TOKEN_KEYWORD_TRUE: case YP_TOKEN_KEYWORD_FALSE: case YP_TOKEN_KEYWORD___FILE__: \ + case YP_TOKEN_KEYWORD___LINE__: case YP_TOKEN_KEYWORD___ENCODING__: case YP_TOKEN_MINUS_GREATER: \ + case YP_TOKEN_HEREDOC_START: case YP_TOKEN_UMINUS_NUM: case YP_TOKEN_CHARACTER_LITERAL + +// This macro allows you to define a case statement for all of the token types +// that could begin a parameter. +#define YP_CASE_PARAMETER YP_TOKEN_AMPERSAND: case YP_TOKEN_UDOT_DOT_DOT: case YP_TOKEN_IDENTIFIER: \ + case YP_TOKEN_LABEL: case YP_TOKEN_USTAR: case YP_TOKEN_STAR: case YP_TOKEN_STAR_STAR: case YP_TOKEN_USTAR_STAR: case YP_TOKEN_CONSTANT: \ + case YP_TOKEN_INSTANCE_VARIABLE: case YP_TOKEN_GLOBAL_VARIABLE: case YP_TOKEN_CLASS_VARIABLE + +// This macro allows you to define a case statement for all of the nodes that +// can be transformed into write targets. +#define YP_CASE_WRITABLE YP_NODE_CLASS_VARIABLE_READ_NODE: case YP_NODE_CONSTANT_PATH_NODE: \ + case YP_NODE_CONSTANT_READ_NODE: case YP_NODE_GLOBAL_VARIABLE_READ_NODE: case YP_NODE_LOCAL_VARIABLE_READ_NODE: \ + case YP_NODE_INSTANCE_VARIABLE_READ_NODE: case YP_NODE_MULTI_WRITE_NODE: case YP_NODE_BACK_REFERENCE_READ_NODE: \ + case YP_NODE_NUMBERED_REFERENCE_READ_NODE + +// Parse a node that is part of a string. If the subsequent tokens cannot be +// parsed as a string part, then NULL is returned. +static yp_node_t * +parse_string_part(yp_parser_t *parser) { + switch (parser->current.type) { + // Here the lexer has returned to us plain string content. In this case + // we'll create a string node that has no opening or closing and return that + // as the part. These kinds of parts look like: + // + // "aaa #{bbb} #@ccc ddd" + // ^^^^ ^ ^^^^ + case YP_TOKEN_STRING_CONTENT: { + yp_unescape_type_t unescape_type = YP_UNESCAPE_ALL; + + if (parser->lex_modes.current->mode == YP_LEX_HEREDOC) { + if (parser->lex_modes.current->as.heredoc.quote == YP_HEREDOC_QUOTE_SINGLE) { + unescape_type = YP_UNESCAPE_MINIMAL; + } + } + + parser_lex(parser); + + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + + return (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &parser->previous, &closing, unescape_type); + } + // Here the lexer has returned the beginning of an embedded expression. In + // that case we'll parse the inner statements and return that as the part. + // These kinds of parts look like: + // + // "aaa #{bbb} #@ccc ddd" + // ^^^^^^ + case YP_TOKEN_EMBEXPR_BEGIN: { + yp_lex_state_t state = parser->lex_state; + int brace_nesting = parser->brace_nesting; + + parser->brace_nesting = 0; + lex_state_set(parser, YP_LEX_STATE_BEG); + parser_lex(parser); + + yp_token_t opening = parser->previous; + yp_statements_node_t *statements = NULL; + + if (!match_type_p(parser, YP_TOKEN_EMBEXPR_END)) { + yp_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, YP_CONTEXT_EMBEXPR); + yp_accepts_block_stack_pop(parser); + } + + parser->brace_nesting = brace_nesting; + lex_state_set(parser, state); + + expect(parser, YP_TOKEN_EMBEXPR_END, "Expected a closing delimiter for an embedded expression."); + yp_token_t closing = parser->previous; + + return (yp_node_t *) yp_embedded_statements_node_create(parser, &opening, statements, &closing); + } + + // Here the lexer has returned the beginning of an embedded variable. + // In that case we'll parse the variable and create an appropriate node + // for it and then return that node. These kinds of parts look like: + // + // "aaa #{bbb} #@ccc ddd" + // ^^^^^ + case YP_TOKEN_EMBVAR: { + lex_state_set(parser, YP_LEX_STATE_BEG); + parser_lex(parser); + + yp_token_t operator = parser->previous; + yp_node_t *variable; + + switch (parser->current.type) { + // In this case a back reference is being interpolated. We'll + // create a global variable read node. + case YP_TOKEN_BACK_REFERENCE: + parser_lex(parser); + variable = (yp_node_t *) yp_back_reference_read_node_create(parser, &parser->previous); + break; + // In this case an nth reference is being interpolated. We'll + // create a global variable read node. + case YP_TOKEN_NUMBERED_REFERENCE: + parser_lex(parser); + variable = (yp_node_t *) yp_numbered_reference_read_node_create(parser, &parser->previous); + break; + // In this case a global variable is being interpolated. We'll + // create a global variable read node. + case YP_TOKEN_GLOBAL_VARIABLE: + parser_lex(parser); + variable = (yp_node_t *) yp_global_variable_read_node_create(parser, &parser->previous); + break; + // In this case an instance variable is being interpolated. + // We'll create an instance variable read node. + case YP_TOKEN_INSTANCE_VARIABLE: + parser_lex(parser); + variable = (yp_node_t *) yp_instance_variable_read_node_create(parser, &parser->previous); + break; + // In this case a class variable is being interpolated. We'll + // create a class variable read node. + case YP_TOKEN_CLASS_VARIABLE: + parser_lex(parser); + variable = (yp_node_t *) yp_class_variable_read_node_create(parser, &parser->previous); + break; + // We can hit here if we got an invalid token. In that case + // we'll not attempt to lex this token and instead just return a + // missing node. + default: + expect(parser, YP_TOKEN_IDENTIFIER, "Expected a valid embedded variable."); + variable = (yp_node_t *) yp_missing_node_create(parser, parser->current.start, parser->current.end); + break; + } + + return (yp_node_t *) yp_embedded_variable_node_create(parser, &operator, variable); + } + default: + parser_lex(parser); + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Could not understand string part"); + return NULL; + } +} + +static yp_node_t * +parse_symbol(yp_parser_t *parser, yp_lex_mode_t *lex_mode, yp_lex_state_t next_state) { + bool lex_string = lex_mode->mode == YP_LEX_STRING; + bool lex_interpolation = lex_string && lex_mode->as.string.interpolation; + yp_token_t opening = parser->previous; + + if (!lex_string) { + if (next_state != YP_LEX_STATE_NONE) { + lex_state_set(parser, next_state); + } + yp_token_t symbol; + + switch (parser->current.type) { + case YP_TOKEN_IDENTIFIER: + case YP_TOKEN_CONSTANT: + case YP_TOKEN_INSTANCE_VARIABLE: + case YP_TOKEN_CLASS_VARIABLE: + case YP_TOKEN_GLOBAL_VARIABLE: + case YP_TOKEN_NUMBERED_REFERENCE: + case YP_TOKEN_BACK_REFERENCE: + case YP_CASE_KEYWORD: + parser_lex(parser); + symbol = parser->previous; + break; + case YP_CASE_OPERATOR: + lex_state_set(parser, next_state == YP_LEX_STATE_NONE ? YP_LEX_STATE_ENDFN : next_state); + parser_lex(parser); + symbol = parser->previous; + break; + default: + expect(parser, YP_TOKEN_IDENTIFIER, "Expected symbol."); + symbol = parser->previous; + break; + } + + yp_token_t closing = not_provided(parser); + return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &symbol, &closing, YP_UNESCAPE_ALL); + } + + // If we weren't in a string in the previous check then we have to be now. + assert(lex_string); + + if (lex_interpolation) { + yp_interpolated_symbol_node_t *interpolated = yp_interpolated_symbol_node_create(parser, &opening, NULL, &opening); + + while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { + yp_node_t *part = parse_string_part(parser); + if (part != NULL) { + yp_interpolated_symbol_node_append(interpolated, part); + } + } + + if (next_state != YP_LEX_STATE_NONE) { + lex_state_set(parser, next_state); + } + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for an interpolated symbol."); + + yp_interpolated_symbol_node_closing_set(interpolated, &parser->previous); + return (yp_node_t *) interpolated; + } + + yp_token_t content; + if (accept(parser, YP_TOKEN_STRING_CONTENT)) { + content = parser->previous; + } else { + content = (yp_token_t) { .type = YP_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end }; + } + + if (next_state != YP_LEX_STATE_NONE) { + lex_state_set(parser, next_state); + } + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a dynamic symbol."); + + return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_ALL); +} + +// Parse an argument to undef which can either be a bare word, a +// symbol, a constant, or an interpolated symbol. +static inline yp_node_t * +parse_undef_argument(yp_parser_t *parser) { + switch (parser->current.type) { + case YP_CASE_KEYWORD: + case YP_CASE_OPERATOR: + case YP_TOKEN_CONSTANT: + case YP_TOKEN_IDENTIFIER: { + parser_lex(parser); + + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + + return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &parser->previous, &closing, YP_UNESCAPE_ALL); + } + case YP_TOKEN_SYMBOL_BEGIN: { + yp_lex_mode_t lex_mode = *parser->lex_modes.current; + parser_lex(parser); + + return parse_symbol(parser, &lex_mode, YP_LEX_STATE_NONE); + } + default: + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Expected a bare word or symbol argument."); + return (yp_node_t *) yp_missing_node_create(parser, parser->current.start, parser->current.end); + } +} + +// Parse an argument to alias which can either be a bare word, a symbol, an +// interpolated symbol or a global variable. If this is the first argument, then +// we need to set the lex state to YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM +// between the first and second arguments. +static inline yp_node_t * +parse_alias_argument(yp_parser_t *parser, bool first) { + switch (parser->current.type) { + case YP_CASE_OPERATOR: + case YP_CASE_KEYWORD: + case YP_TOKEN_CONSTANT: + case YP_TOKEN_IDENTIFIER: { + if (first) { + lex_state_set(parser, YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM); + } + + parser_lex(parser); + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + + return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &parser->previous, &closing, YP_UNESCAPE_ALL); + } + case YP_TOKEN_SYMBOL_BEGIN: { + yp_lex_mode_t lex_mode = *parser->lex_modes.current; + parser_lex(parser); + + return parse_symbol(parser, &lex_mode, first ? YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM : YP_LEX_STATE_NONE); + } + case YP_TOKEN_BACK_REFERENCE: + parser_lex(parser); + return (yp_node_t *) yp_back_reference_read_node_create(parser, &parser->previous); + case YP_TOKEN_NUMBERED_REFERENCE: + parser_lex(parser); + return (yp_node_t *) yp_numbered_reference_read_node_create(parser, &parser->previous); + case YP_TOKEN_GLOBAL_VARIABLE: + parser_lex(parser); + return (yp_node_t *) yp_global_variable_read_node_create(parser, &parser->previous); + default: + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Expected a bare word, symbol or global variable argument."); + return (yp_node_t *) yp_missing_node_create(parser, parser->current.start, parser->current.end); + } +} + +// Parse an identifier into either a local variable read or a call. +static yp_node_t * +parse_vcall(yp_parser_t *parser) { + int depth; + + if ( + (parser->current.type != YP_TOKEN_PARENTHESIS_LEFT) && + (parser->previous.end[-1] != '!') && + (parser->previous.end[-1] != '?') && + (depth = yp_parser_local_depth(parser, &parser->previous)) != -1 + ) { + return (yp_node_t *) yp_local_variable_read_node_create(parser, &parser->previous, (uint32_t) depth); + } + + return (yp_node_t *) yp_call_node_vcall_create(parser, &parser->previous); +} + +static inline yp_token_t +parse_method_definition_name(yp_parser_t *parser) { + switch (parser->current.type) { + case YP_CASE_KEYWORD: + case YP_TOKEN_CONSTANT: + case YP_TOKEN_IDENTIFIER: + parser_lex(parser); + return parser->previous; + case YP_CASE_OPERATOR: + lex_state_set(parser, YP_LEX_STATE_ENDFN); + parser_lex(parser); + return parser->previous; + default: + return not_provided(parser); + } +} + +// Calculate the common leading whitespace for each line in a heredoc. +static int +parse_heredoc_common_whitespace(yp_parser_t *parser, yp_node_list_t *nodes) { + int common_whitespace = -1; + + for (size_t index = 0; index < nodes->size; index++) { + yp_node_t *node = nodes->nodes[index]; + + if (node->type != YP_NODE_STRING_NODE) continue; + yp_location_t *content_loc = &((yp_string_node_t *) node)->content_loc; + + // If the previous node wasn't a string node, we don't want to trim + // whitespace. This could happen after an interpolated expression or + // variable. + if (index == 0 || nodes->nodes[index - 1]->type == YP_NODE_STRING_NODE) { + int cur_whitespace; + const char *cur_char = content_loc->start; + + while (cur_char && cur_char < content_loc->end) { + // Any empty newlines aren't included in the minimum whitespace calculation + while (cur_char < content_loc->end && *cur_char == '\n') cur_char++; + while (cur_char + 1 < content_loc->end && *cur_char == '\r' && cur_char[1] == '\n') cur_char += 2; + + if (cur_char == content_loc->end) break; + + cur_whitespace = 0; + + while (yp_char_is_inline_whitespace(*cur_char) && cur_char < content_loc->end) { + if (cur_char[0] == '\t') { + cur_whitespace = (cur_whitespace / YP_TAB_WHITESPACE_SIZE + 1) * YP_TAB_WHITESPACE_SIZE; + } else { + cur_whitespace++; + } + cur_char++; + } + + // If we hit a newline, then we have encountered a line that contains + // only whitespace, and it shouldn't be considered in the calculation of + // common leading whitespace. + if (*cur_char == '\n') { + cur_char++; + continue; + } + + if (cur_whitespace < common_whitespace || common_whitespace == -1) { + common_whitespace = cur_whitespace; + } + + cur_char = next_newline(cur_char + 1, parser->end - (cur_char + 1)); + if (cur_char) cur_char++; + } + } + } + + return common_whitespace; +} + +// Take a heredoc node that is indented by a ~ and trim the leading whitespace. +static void +parse_heredoc_dedent(yp_parser_t *parser, yp_node_t *node, yp_heredoc_quote_t quote) { + yp_node_list_t *nodes; + + if (quote == YP_HEREDOC_QUOTE_BACKTICK) { + nodes = &((yp_interpolated_x_string_node_t *) node)->parts; + } else { + nodes = &((yp_interpolated_string_node_t *) node)->parts; + } + + // First, calculate how much common whitespace we need to trim. If there is + // none or it's 0, then we can return early. + int common_whitespace; + if ((common_whitespace = parse_heredoc_common_whitespace(parser, nodes)) <= 0) return; + + // Iterate over all nodes, and trim whitespace accordingly. + for (size_t index = 0; index < nodes->size; index++) { + yp_node_t *node = nodes->nodes[index]; + if (node->type != YP_NODE_STRING_NODE) continue; + + // Get a reference to the string struct that is being held by the string + // node. This is the value we're going to actual manipulate. + yp_string_t *string = &((yp_string_node_t *) node)->unescaped; + yp_string_ensure_owned(string); + + // Now get the bounds of the existing string. We'll use this as a + // destination to move bytes into. We'll also use it for bounds checking + // since we don't require that these strings be null terminated. + size_t dest_length = string->as.owned.length; + char *source_start = string->as.owned.source; + + const char *source_cursor = source_start; + const char *source_end = source_cursor + dest_length; + + // We're going to move bytes backward in the string when we get leading + // whitespace, so we'll maintain a pointer to the current position in the + // string that we're writing to. + char *dest_cursor = source_start; + bool dedent_next = (index == 0) || (nodes->nodes[index - 1]->type == YP_NODE_STRING_NODE); + + while (source_cursor < source_end) { + // If we need to dedent the next element within the heredoc or the next + // line within the string node, then we'll do it here. + if (dedent_next) { + int trimmed_whitespace = 0; + + // While we haven't reached the amount of common whitespace that we need + // to trim and we haven't reached the end of the string, we'll keep + // trimming whitespace. Trimming in this context means skipping over + // these bytes such that they aren't copied into the new string. + while ((source_cursor < source_end) && yp_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) { + if (*source_cursor == '\t') { + trimmed_whitespace = (trimmed_whitespace / YP_TAB_WHITESPACE_SIZE + 1) * YP_TAB_WHITESPACE_SIZE; + if (trimmed_whitespace > common_whitespace) break; + } else { + trimmed_whitespace++; + } + + source_cursor++; + dest_length--; + } + } + + // At this point we have dedented all that we need to, so we need to find + // the next newline. + const char *breakpoint = next_newline(source_cursor, source_end - source_cursor); + + if (breakpoint == NULL) { + // If there isn't another newline, then we can just move the rest of the + // string and break from the loop. + memmove(dest_cursor, source_cursor, (size_t) (source_end - source_cursor)); + break; + } + + // Otherwise, we need to move everything including the newline, and + // then set the dedent_next flag to true. + if (breakpoint < source_end) breakpoint++; + memmove(dest_cursor, source_cursor, (size_t) (breakpoint - source_cursor)); + dest_cursor += (breakpoint - source_cursor); + source_cursor = breakpoint; + dedent_next = true; + } + + string->as.owned.length = dest_length; + } +} + +static yp_node_t * +parse_pattern(yp_parser_t *parser, bool top_pattern, const char *message); + +// Accept any number of constants joined by :: delimiters. +static yp_node_t * +parse_pattern_constant_path(yp_parser_t *parser, yp_node_t *node) { + // Now, if there are any :: operators that follow, parse them as constant + // path nodes. + while (accept(parser, YP_TOKEN_COLON_COLON)) { + yp_token_t delimiter = parser->previous; + expect(parser, YP_TOKEN_CONSTANT, "Expected a constant after the :: operator."); + + yp_node_t *child = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); + node = (yp_node_t *)yp_constant_path_node_create(parser, node, &delimiter, child); + } + + // If there is a [ or ( that follows, then this is part of a larger pattern + // expression. We'll parse the inner pattern here, then modify the returned + // inner pattern with our constant path attached. + if (!match_any_type_p(parser, 2, YP_TOKEN_BRACKET_LEFT, YP_TOKEN_PARENTHESIS_LEFT)) { + return node; + } + + yp_token_t opening; + yp_token_t closing; + yp_node_t *inner = NULL; + + if (accept(parser, YP_TOKEN_BRACKET_LEFT)) { + opening = parser->previous; + accept(parser, YP_TOKEN_NEWLINE); + + if (!accept(parser, YP_TOKEN_BRACKET_RIGHT)) { + inner = parse_pattern(parser, true, "Expected a pattern expression after the [ operator."); + accept(parser, YP_TOKEN_NEWLINE); + expect(parser, YP_TOKEN_BRACKET_RIGHT, "Expected a ] to close the pattern expression."); + } + + closing = parser->previous; + } else { + parser_lex(parser); + opening = parser->previous; + + if (!accept(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { + inner = parse_pattern(parser, true, "Expected a pattern expression after the ( operator."); + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a ) to close the pattern expression."); + } + + closing = parser->previous; + } + + if (!inner) { + // If there was no inner pattern, then we have something like Foo() or + // Foo[]. In that case we'll create an array pattern with no requireds. + return (yp_node_t *) yp_array_pattern_node_constant_create(parser, node, &opening, &closing); + } + + // Now that we have the inner pattern, check to see if it's an array, find, + // or hash pattern. If it is, then we'll attach our constant path to it if + // it doesn't already have a constant. If it's not one of those node types + // or it does have a constant, then we'll create an array pattern. + switch (inner->type) { + case YP_NODE_ARRAY_PATTERN_NODE: { + yp_array_pattern_node_t *pattern_node = (yp_array_pattern_node_t *) inner; + + if (pattern_node->constant == NULL) { + pattern_node->base.location.start = node->location.start; + pattern_node->base.location.end = closing.end; + + pattern_node->constant = node; + pattern_node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; + pattern_node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; + + return (yp_node_t *) pattern_node; + } + + break; + } + case YP_NODE_FIND_PATTERN_NODE: { + yp_find_pattern_node_t *pattern_node = (yp_find_pattern_node_t *) inner; + + if (pattern_node->constant == NULL) { + pattern_node->base.location.start = node->location.start; + pattern_node->base.location.end = closing.end; + + pattern_node->constant = node; + pattern_node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; + pattern_node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; + + return (yp_node_t *) pattern_node; + } + + break; + } + case YP_NODE_HASH_PATTERN_NODE: { + yp_hash_pattern_node_t *pattern_node = (yp_hash_pattern_node_t *) inner; + + if (pattern_node->constant == NULL) { + pattern_node->base.location.start = node->location.start; + pattern_node->base.location.end = closing.end; + + pattern_node->constant = node; + pattern_node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; + pattern_node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; + + return (yp_node_t *) pattern_node; + } + + break; + } + default: + break; + } + + // If we got here, then we didn't return one of the inner patterns by + // attaching its constant. In this case we'll create an array pattern and + // attach our constant to it. + yp_array_pattern_node_t *pattern_node = yp_array_pattern_node_constant_create(parser, node, &opening, &closing); + yp_array_pattern_node_requireds_append(pattern_node, inner); + return (yp_node_t *) pattern_node; +} + +// Parse a rest pattern. +static yp_splat_node_t * +parse_pattern_rest(yp_parser_t *parser) { + assert(parser->previous.type == YP_TOKEN_USTAR); + yp_token_t operator = parser->previous; + yp_node_t *name = NULL; + + // Rest patterns don't necessarily have a name associated with them. So we + // will check for that here. If they do, then we'll add it to the local table + // since this pattern will cause it to become a local variable. + if (accept(parser, YP_TOKEN_IDENTIFIER)) { + yp_token_t identifier = parser->previous; + yp_parser_local_add_token(parser, &identifier); + name = (yp_node_t *) yp_local_variable_target_node_create(parser, &identifier); + } + + // Finally we can return the created node. + return yp_splat_node_create(parser, &operator, name); +} + +// Parse a keyword rest node. +static yp_node_t * +parse_pattern_keyword_rest(yp_parser_t *parser) { + assert(parser->current.type == YP_TOKEN_USTAR_STAR); + parser_lex(parser); + + yp_token_t operator = parser->previous; + yp_node_t *value = NULL; + + if (accept(parser, YP_TOKEN_KEYWORD_NIL)) { + return (yp_node_t *) yp_no_keywords_parameter_node_create(parser, &operator, &parser->previous); + } + + if (accept(parser, YP_TOKEN_IDENTIFIER)) { + yp_parser_local_add_token(parser, &parser->previous); + value = (yp_node_t *) yp_local_variable_target_node_create(parser, &parser->previous); + } + + return (yp_node_t *) yp_assoc_splat_node_create(parser, value, &operator); +} + +// Parse a hash pattern. +static yp_hash_pattern_node_t * +parse_pattern_hash(yp_parser_t *parser, yp_node_t *first_assoc) { + if (first_assoc->type == YP_NODE_ASSOC_NODE) { + if (!match_any_type_p(parser, 7, YP_TOKEN_COMMA, YP_TOKEN_KEYWORD_THEN, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_PARENTHESIS_RIGHT, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { + // Here we have a value for the first assoc in the list, so we will parse it + // now and update the first assoc. + yp_node_t *value = parse_pattern(parser, false, "Expected a pattern expression after the key."); + + yp_assoc_node_t *assoc = (yp_assoc_node_t *) first_assoc; + assoc->base.location.end = value->location.end; + assoc->value = value; + } else { + yp_node_t *key = ((yp_assoc_node_t *) first_assoc)->key; + + if (key->type == YP_NODE_SYMBOL_NODE) { + yp_location_t *value_loc = &((yp_symbol_node_t *) key)->value_loc; + yp_parser_local_add_location(parser, value_loc->start, value_loc->end); + } + } + } + + yp_node_list_t assocs = YP_EMPTY_NODE_LIST; + yp_node_list_append(&assocs, first_assoc); + + // If there are any other assocs, then we'll parse them now. + while (accept(parser, YP_TOKEN_COMMA)) { + // Here we need to break to support trailing commas. + if (match_any_type_p(parser, 6, YP_TOKEN_KEYWORD_THEN, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_PARENTHESIS_RIGHT, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { + break; + } + + yp_node_t *assoc; + + if (match_type_p(parser, YP_TOKEN_USTAR_STAR)) { + assoc = parse_pattern_keyword_rest(parser); + } else { + expect(parser, YP_TOKEN_LABEL, "Expected a label after the `,'."); + yp_node_t *key = (yp_node_t *) yp_symbol_node_label_create(parser, &parser->previous); + yp_node_t *value = NULL; + + if (!match_any_type_p(parser, 7, YP_TOKEN_COMMA, YP_TOKEN_KEYWORD_THEN, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_PARENTHESIS_RIGHT, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { + value = parse_pattern(parser, false, "Expected a pattern expression after the key."); + } else { + yp_location_t *value_loc = &((yp_symbol_node_t *) key)->value_loc; + yp_parser_local_add_location(parser, value_loc->start, value_loc->end); + } + + yp_token_t operator = not_provided(parser); + assoc = (yp_node_t *) yp_assoc_node_create(parser, key, &operator, value); + } + + yp_node_list_append(&assocs, assoc); + } + + yp_hash_pattern_node_t *node = yp_hash_pattern_node_node_list_create(parser, &assocs); + free(assocs.nodes); + + return node; +} + +// Parse a pattern expression primitive. +static yp_node_t * +parse_pattern_primitive(yp_parser_t *parser, const char *message) { + switch (parser->current.type) { + case YP_TOKEN_IDENTIFIER: { + parser_lex(parser); + yp_parser_local_add_token(parser, &parser->previous); + return (yp_node_t *) yp_local_variable_target_node_create(parser, &parser->previous); + } + case YP_TOKEN_BRACKET_LEFT_ARRAY: { + yp_token_t opening = parser->current; + parser_lex(parser); + + if (accept(parser, YP_TOKEN_BRACKET_RIGHT)) { + // If we have an empty array pattern, then we'll just return a new + // array pattern node. + return (yp_node_t *)yp_array_pattern_node_empty_create(parser, &opening, &parser->previous); + } + + // Otherwise, we'll parse the inner pattern, then deal with it depending + // on the type it returns. + yp_node_t *inner = parse_pattern(parser, true, "Expected a pattern expression after the [ operator."); + + accept(parser, YP_TOKEN_NEWLINE); + + expect(parser, YP_TOKEN_BRACKET_RIGHT, "Expected a ] to close the pattern expression."); + yp_token_t closing = parser->previous; + + switch (inner->type) { + case YP_NODE_ARRAY_PATTERN_NODE: { + yp_array_pattern_node_t *pattern_node = (yp_array_pattern_node_t *) inner; + if (pattern_node->opening_loc.start == NULL) { + pattern_node->base.location.start = opening.start; + pattern_node->base.location.end = closing.end; + + pattern_node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; + pattern_node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; + + return (yp_node_t *) pattern_node; + } + + break; + } + case YP_NODE_FIND_PATTERN_NODE: { + yp_find_pattern_node_t *pattern_node = (yp_find_pattern_node_t *) inner; + if (pattern_node->opening_loc.start == NULL) { + pattern_node->base.location.start = opening.start; + pattern_node->base.location.end = closing.end; + + pattern_node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; + pattern_node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; + + return (yp_node_t *) pattern_node; + } + + break; + } + default: + break; + } + + yp_array_pattern_node_t *node = yp_array_pattern_node_empty_create(parser, &opening, &closing); + yp_array_pattern_node_requireds_append(node, inner); + return (yp_node_t *) node; + } + case YP_TOKEN_BRACE_LEFT: { + bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; + parser->pattern_matching_newlines = false; + + yp_hash_pattern_node_t *node; + yp_token_t opening = parser->current; + parser_lex(parser); + + if (accept(parser, YP_TOKEN_BRACE_RIGHT)) { + // If we have an empty hash pattern, then we'll just return a new hash + // pattern node. + node = yp_hash_pattern_node_empty_create(parser, &opening, &parser->previous); + } else { + yp_node_t *key; + + switch (parser->current.type) { + case YP_TOKEN_LABEL: + parser_lex(parser); + key = (yp_node_t *) yp_symbol_node_label_create(parser, &parser->previous); + break; + case YP_TOKEN_USTAR_STAR: + key = parse_pattern_keyword_rest(parser); + break; + case YP_TOKEN_STRING_BEGIN: + key = parse_expression(parser, YP_BINDING_POWER_MAX, "Expected a key in the hash pattern."); + if (!yp_symbol_node_label_p(key)) { + yp_diagnostic_list_append(&parser->error_list, key->location.start, key->location.end, "Expected a label as the key in the hash pattern."); + } + + break; + default: + parser_lex(parser); + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Expected a key in the hash pattern."); + key = (yp_node_t *) yp_missing_node_create(parser, parser->previous.start, parser->previous.end); + break; + } + + yp_token_t operator = not_provided(parser); + node = parse_pattern_hash(parser, (yp_node_t *) yp_assoc_node_create(parser, key, &operator, NULL)); + + accept(parser, YP_TOKEN_NEWLINE); + expect(parser, YP_TOKEN_BRACE_RIGHT, "Expected a } to close the pattern expression."); + yp_token_t closing = parser->previous; + + node->base.location.start = opening.start; + node->base.location.end = closing.end; + + node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; + node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; + } + + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + return (yp_node_t *) node; + } + case YP_TOKEN_UDOT_DOT: + case YP_TOKEN_UDOT_DOT_DOT: { + yp_token_t operator = parser->current; + parser_lex(parser); + + // Since we have a unary range operator, we need to parse the subsequent + // expression as the right side of the range. + switch (parser->current.type) { + case YP_CASE_PRIMITIVE: { + yp_node_t *right = parse_expression(parser, YP_BINDING_POWER_MAX, "Expected an expression after the range operator."); + return (yp_node_t *) yp_range_node_create(parser, NULL, &operator, right); + } + default: { + yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "Expected an expression after the range operator."); + yp_node_t *right = (yp_node_t *) yp_missing_node_create(parser, operator.start, operator.end); + return (yp_node_t *) yp_range_node_create(parser, NULL, &operator, right); + } + } + } + case YP_CASE_PRIMITIVE: { + yp_node_t *node = parse_expression(parser, YP_BINDING_POWER_MAX, message); + + // Now that we have a primitive, we need to check if it's part of a range. + if (accept_any(parser, 2, YP_TOKEN_DOT_DOT, YP_TOKEN_DOT_DOT_DOT)) { + yp_token_t operator = parser->previous; + + // Now that we have the operator, we need to check if this is followed + // by another expression. If it is, then we will create a full range + // node. Otherwise, we'll create an endless range. + switch (parser->current.type) { + case YP_CASE_PRIMITIVE: { + yp_node_t *right = parse_expression(parser, YP_BINDING_POWER_MAX, "Expected an expression after the range operator."); + return (yp_node_t *) yp_range_node_create(parser, node, &operator, right); + } + default: + return (yp_node_t *) yp_range_node_create(parser, node, &operator, NULL); + } + } + + return node; + } + case YP_TOKEN_CARET: { + parser_lex(parser); + yp_token_t operator = parser->previous; + + // At this point we have a pin operator. We need to check the subsequent + // expression to determine if it's a variable or an expression. + switch (parser->current.type) { + case YP_TOKEN_IDENTIFIER: { + parser_lex(parser); + yp_node_t *variable = (yp_node_t *) yp_local_variable_read_node_create(parser, &parser->previous, 0); + + return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); + } + case YP_TOKEN_INSTANCE_VARIABLE: { + parser_lex(parser); + yp_node_t *variable = (yp_node_t *) yp_instance_variable_read_node_create(parser, &parser->previous); + + return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); + } + case YP_TOKEN_CLASS_VARIABLE: { + parser_lex(parser); + yp_node_t *variable = (yp_node_t *) yp_class_variable_read_node_create(parser, &parser->previous); + + return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); + } + case YP_TOKEN_GLOBAL_VARIABLE: { + parser_lex(parser); + yp_node_t *variable = (yp_node_t *) yp_global_variable_read_node_create(parser, &parser->previous); + + return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); + } + case YP_TOKEN_NUMBERED_REFERENCE: { + parser_lex(parser); + yp_node_t *variable = (yp_node_t *) yp_numbered_reference_read_node_create(parser, &parser->previous); + + return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); + } + case YP_TOKEN_BACK_REFERENCE: { + parser_lex(parser); + yp_node_t *variable = (yp_node_t *) yp_back_reference_read_node_create(parser, &parser->previous); + + return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); + } + case YP_TOKEN_PARENTHESIS_LEFT: { + bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; + parser->pattern_matching_newlines = false; + + yp_token_t lparen = parser->current; + parser_lex(parser); + + yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected an expression after the pin operator."); + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + + accept(parser, YP_TOKEN_NEWLINE); + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a closing parenthesis after the expression."); + return (yp_node_t *) yp_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous); + } + default: { + // If we get here, then we have a pin operator followed by something + // not understood. We'll create a missing node and return that. + yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "Expected a variable after the pin operator."); + yp_node_t *variable = (yp_node_t *) yp_missing_node_create(parser, operator.start, operator.end); + return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); + } + } + } + case YP_TOKEN_UCOLON_COLON: { + yp_token_t delimiter = parser->current; + parser_lex(parser); + + expect(parser, YP_TOKEN_CONSTANT, "Expected a constant after the :: operator."); + yp_node_t *child = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); + yp_constant_path_node_t *node = yp_constant_path_node_create(parser, NULL, &delimiter, child); + + return parse_pattern_constant_path(parser, (yp_node_t *)node); + } + case YP_TOKEN_CONSTANT: { + yp_token_t constant = parser->current; + parser_lex(parser); + + yp_node_t *node = (yp_node_t *) yp_constant_read_node_create(parser, &constant); + return parse_pattern_constant_path(parser, node); + } + default: + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, message); + return (yp_node_t *) yp_missing_node_create(parser, parser->current.start, parser->current.end); + } +} + +// Parse any number of primitives joined by alternation and ended optionally by +// assignment. +static yp_node_t * +parse_pattern_primitives(yp_parser_t *parser, const char *message) { + yp_node_t *node = NULL; + + do { + yp_token_t operator = parser->previous; + + switch (parser->current.type) { + case YP_TOKEN_IDENTIFIER: + case YP_TOKEN_BRACKET_LEFT_ARRAY: + case YP_TOKEN_BRACE_LEFT: + case YP_TOKEN_CARET: + case YP_TOKEN_CONSTANT: + case YP_TOKEN_UCOLON_COLON: + case YP_TOKEN_UDOT_DOT: + case YP_TOKEN_UDOT_DOT_DOT: + case YP_CASE_PRIMITIVE: { + if (node == NULL) { + node = parse_pattern_primitive(parser, message); + } else { + yp_node_t *right = parse_pattern_primitive(parser, "Expected to be able to parse a pattern after `|'."); + node = (yp_node_t *) yp_alternation_pattern_node_create(parser, node, right, &operator); + } + + break; + } + case YP_TOKEN_PARENTHESIS_LEFT: { + parser_lex(parser); + if (node != NULL) { + yp_node_destroy(parser, node); + } + node = parse_pattern(parser, false, "Expected a pattern after the opening parenthesis."); + + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a closing parenthesis after the pattern."); + break; + } + default: { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, message); + yp_node_t *right = (yp_node_t *) yp_missing_node_create(parser, parser->current.start, parser->current.end); + + if (node == NULL) { + node = right; + } else { + node = (yp_node_t *) yp_alternation_pattern_node_create(parser, node, right, &operator); + } + + break; + } + } + } while (accept(parser, YP_TOKEN_PIPE)); + + // If we have an =>, then we are assigning this pattern to a variable. + // In this case we should create an assignment node. + while (accept(parser, YP_TOKEN_EQUAL_GREATER)) { + yp_token_t operator = parser->previous; + + expect(parser, YP_TOKEN_IDENTIFIER, "Expected an identifier after the `=>' operator."); + yp_token_t identifier = parser->previous; + yp_parser_local_add_token(parser, &identifier); + + yp_node_t *target = (yp_node_t *) yp_local_variable_target_node_create(parser, &identifier); + node = (yp_node_t *) yp_capture_pattern_node_create(parser, node, target, &operator); + } + + return node; +} + +// Parse a pattern matching expression. +static yp_node_t * +parse_pattern(yp_parser_t *parser, bool top_pattern, const char *message) { + yp_node_t *node = NULL; + + bool leading_rest = false; + bool trailing_rest = false; + + switch (parser->current.type) { + case YP_TOKEN_LABEL: { + parser_lex(parser); + yp_node_t *key = (yp_node_t *) yp_symbol_node_label_create(parser, &parser->previous); + yp_token_t operator = not_provided(parser); + + return (yp_node_t *) parse_pattern_hash(parser, (yp_node_t *) yp_assoc_node_create(parser, key, &operator, NULL)); + } + case YP_TOKEN_USTAR_STAR: { + node = parse_pattern_keyword_rest(parser); + return (yp_node_t *) parse_pattern_hash(parser, node); + } + case YP_TOKEN_USTAR: { + if (top_pattern) { + parser_lex(parser); + node = (yp_node_t *) parse_pattern_rest(parser); + leading_rest = true; + break; + } + } + /* fallthrough */ + default: + node = parse_pattern_primitives(parser, message); + break; + } + + // If we got a dynamic label symbol, then we need to treat it like the + // beginning of a hash pattern. + if (yp_symbol_node_label_p(node)) { + yp_token_t operator = not_provided(parser); + return (yp_node_t *) parse_pattern_hash(parser, (yp_node_t *) yp_assoc_node_create(parser, node, &operator, NULL)); + } + + if (top_pattern && match_type_p(parser, YP_TOKEN_COMMA)) { + // If we have a comma, then we are now parsing either an array pattern or a + // find pattern. We need to parse all of the patterns, put them into a big + // list, and then determine which type of node we have. + yp_node_list_t nodes = YP_EMPTY_NODE_LIST; + yp_node_list_append(&nodes, node); + + // Gather up all of the patterns into the list. + while (accept(parser, YP_TOKEN_COMMA)) { + // Break early here in case we have a trailing comma. + if (match_any_type_p(parser, 5, YP_TOKEN_KEYWORD_THEN, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { + break; + } + + if (accept(parser, YP_TOKEN_USTAR)) { + node = (yp_node_t *) parse_pattern_rest(parser); + + // If we have already parsed a splat pattern, then this is an error. We + // will continue to parse the rest of the patterns, but we will indicate + // it as an error. + if (trailing_rest) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected rest pattern."); + } + + trailing_rest = true; + } else { + node = parse_pattern_primitives(parser, "Expected a pattern after the comma."); + } + + yp_node_list_append(&nodes, node); + } + + // If the first pattern and the last pattern are rest patterns, then we will + // call this a find pattern, regardless of how many rest patterns are in + // between because we know we already added the appropriate errors. + // Otherwise we will create an array pattern. + if (nodes.nodes[0]->type == YP_NODE_SPLAT_NODE && nodes.nodes[nodes.size - 1]->type == YP_NODE_SPLAT_NODE) { + node = (yp_node_t *) yp_find_pattern_node_create(parser, &nodes); + } else { + node = (yp_node_t *) yp_array_pattern_node_node_list_create(parser, &nodes); + } + + free(nodes.nodes); + } else if (leading_rest) { + // Otherwise, if we parsed a single splat pattern, then we know we have an + // array pattern, so we can go ahead and create that node. + node = (yp_node_t *) yp_array_pattern_node_rest_create(parser, node); + } + + return node; +} + +// Parse an expression that begins with the previous node that we just lexed. +static inline yp_node_t * +parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) { + switch (parser->current.type) { + case YP_TOKEN_BRACKET_LEFT_ARRAY: { + parser_lex(parser); + + yp_array_node_t *array = yp_array_node_create(parser, &parser->previous); + yp_accepts_block_stack_push(parser, true); + bool parsed_bare_hash = false; + + while (!match_any_type_p(parser, 2, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_EOF)) { + // Handle the case where we don't have a comma and we have a newline followed by a right bracket. + if (accept(parser, YP_TOKEN_NEWLINE) && match_type_p(parser, YP_TOKEN_BRACKET_RIGHT)) { + break; + } + + if (yp_array_node_size(array) != 0) { + expect(parser, YP_TOKEN_COMMA, "Expected a separator for the elements in an array."); + } + + // If we have a right bracket immediately following a comma, this is + // allowed since it's a trailing comma. In this case we can break out of + // the loop. + if (match_type_p(parser, YP_TOKEN_BRACKET_RIGHT)) break; + + yp_node_t *element; + + if (accept(parser, YP_TOKEN_USTAR)) { + yp_token_t operator = parser->previous; + yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected an expression after '*' in the array."); + element = (yp_node_t *) yp_splat_node_create(parser, &operator, expression); + } else if (match_any_type_p(parser, 2, YP_TOKEN_LABEL, YP_TOKEN_USTAR_STAR)) { + if (parsed_bare_hash) { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unexpected bare hash."); + } + + yp_keyword_hash_node_t *hash = yp_keyword_hash_node_create(parser); + element = (yp_node_t *)hash; + + if (!match_any_type_p(parser, 8, YP_TOKEN_EOF, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON, YP_TOKEN_EOF, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_KEYWORD_DO, YP_TOKEN_PARENTHESIS_RIGHT)) { + parse_assocs(parser, (yp_node_t *) hash); + } + + parsed_bare_hash = true; + } else { + element = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected an element for the array."); + + if (yp_symbol_node_label_p(element) || accept(parser, YP_TOKEN_EQUAL_GREATER)) { + if (parsed_bare_hash) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected bare hash."); + } + + yp_keyword_hash_node_t *hash = yp_keyword_hash_node_create(parser); + + yp_token_t operator; + if (parser->previous.type == YP_TOKEN_EQUAL_GREATER) { + operator = parser->previous; + } else { + operator = not_provided(parser); + } + + yp_node_t *value = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value in the hash literal."); + yp_node_t *assoc = (yp_node_t *) yp_assoc_node_create(parser, element, &operator, value); + yp_keyword_hash_node_elements_append(hash, assoc); + + element = (yp_node_t *)hash; + if (accept(parser, YP_TOKEN_COMMA) && !match_type_p(parser, YP_TOKEN_BRACKET_RIGHT)) { + parse_assocs(parser, (yp_node_t *) hash); + } + + parsed_bare_hash = true; + } + } + + yp_array_node_elements_append(array, element); + if (element->type == YP_NODE_MISSING_NODE) break; + } + + accept(parser, YP_TOKEN_NEWLINE); + expect(parser, YP_TOKEN_BRACKET_RIGHT, "Expected a closing bracket for the array."); + yp_array_node_close_set(array, &parser->previous); + yp_accepts_block_stack_pop(parser); + + return (yp_node_t *) array; + } + case YP_TOKEN_PARENTHESIS_LEFT: + case YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { + parser_lex(parser); + + yp_token_t opening = parser->previous; + while (accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE)); + + // If this is the end of the file or we match a right parenthesis, then + // we have an empty parentheses node, and we can immediately return. + if (match_any_type_p(parser, 2, YP_TOKEN_PARENTHESIS_RIGHT, YP_TOKEN_EOF)) { + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a closing parenthesis."); + return (yp_node_t *) yp_parentheses_node_create(parser, &opening, NULL, &parser->previous); + } + + // Otherwise, we're going to parse the first statement in the list of + // statements within the parentheses. + yp_accepts_block_stack_push(parser, true); + yp_node_t *statement = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected to be able to parse an expression."); + while (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)); + + // If we hit a right parenthesis, then we're done parsing the parentheses + // node, and we can check which kind of node we should return. + if (accept(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { + yp_accepts_block_stack_pop(parser); + + // If we have a single statement and are ending on a right parenthesis, + // then we need to check if this is possibly a multiple assignment node. + if (binding_power == YP_BINDING_POWER_STATEMENT && statement->type == YP_NODE_MULTI_WRITE_NODE) { + yp_multi_write_node_t *multi_statement = (yp_multi_write_node_t *) statement; + + if (multi_statement->value == NULL) { + yp_location_t lparen_loc = { .start = opening.start, .end = opening.end }; + yp_location_t rparen_loc = { .start = parser->previous.start, .end = parser->previous.end }; + yp_multi_write_node_t *multi_write; + + if (multi_statement->lparen_loc.start == NULL) { + multi_write = (yp_multi_write_node_t *) statement; + multi_write->lparen_loc = lparen_loc; + multi_write->rparen_loc = rparen_loc; + } else { + yp_token_t operator = not_provided(parser); + multi_write = yp_multi_write_node_create(parser, &operator, NULL, &lparen_loc, &rparen_loc); + yp_multi_write_node_targets_append(multi_write, statement); + } + + return parse_targets(parser, (yp_node_t *) multi_write, YP_BINDING_POWER_INDEX); + } + } + + // If we have a single statement and are ending on a right parenthesis + // and we didn't return a multiple assignment node, then we can return a + // regular parentheses node now. + yp_statements_node_t *statements = yp_statements_node_create(parser); + yp_statements_node_body_append(statements, statement); + + return (yp_node_t *) yp_parentheses_node_create(parser, &opening, (yp_node_t *) statements, &parser->previous); + } + + // If we have more than one statement in the set of parentheses, then we + // are going to parse all of them as a list of statements. We'll do that + // here. + context_push(parser, YP_CONTEXT_PARENS); + yp_statements_node_t *statements = yp_statements_node_create(parser); + yp_statements_node_body_append(statements, statement); + + while (!match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { + // Ignore semicolon without statements before them + if (accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE)) continue; + + yp_node_t *node = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected to be able to parse an expression."); + yp_statements_node_body_append(statements, node); + + // If we're recovering from a syntax error, then we need to stop parsing the + // statements now. + if (parser->recovering) { + // If this is the level of context where the recovery has happened, then + // we can mark the parser as done recovering. + if (match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false; + break; + } + + if (!accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) break; + } + + context_pop(parser); + yp_accepts_block_stack_pop(parser); + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a closing parenthesis."); + + return (yp_node_t *) yp_parentheses_node_create(parser, &opening, (yp_node_t *) statements, &parser->previous); + } + case YP_TOKEN_BRACE_LEFT: { + yp_accepts_block_stack_push(parser, true); + parser_lex(parser); + yp_hash_node_t *node = yp_hash_node_create(parser, &parser->previous); + + if (!match_any_type_p(parser, 2, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_EOF)) { + parse_assocs(parser, (yp_node_t *) node); + accept(parser, YP_TOKEN_NEWLINE); + } + + yp_accepts_block_stack_pop(parser); + expect(parser, YP_TOKEN_BRACE_RIGHT, "Expected a closing delimiter for a hash literal."); + yp_hash_node_closing_loc_set(node, &parser->previous); + + return (yp_node_t *) node; + } + case YP_TOKEN_CHARACTER_LITERAL: { + parser_lex(parser); + + yp_token_t opening = parser->previous; + opening.type = YP_TOKEN_STRING_BEGIN; + opening.end = opening.start + 1; + + yp_token_t content = parser->previous; + content.type = YP_TOKEN_STRING_CONTENT; + content.start = content.start + 1; + + yp_token_t closing = not_provided(parser); + + return (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &content, &closing, YP_UNESCAPE_ALL); + } + case YP_TOKEN_CLASS_VARIABLE: { + parser_lex(parser); + yp_node_t *node = (yp_node_t *) yp_class_variable_read_node_create(parser, &parser->previous); + + if (binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { + node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); + } + + return node; + } + case YP_TOKEN_CONSTANT: { + parser_lex(parser); + yp_token_t constant = parser->previous; + + // If a constant is immediately followed by parentheses, then this is in + // fact a method call, not a constant read. + if ( + match_type_p(parser, YP_TOKEN_PARENTHESIS_LEFT) || + (binding_power <= YP_BINDING_POWER_ASSIGNMENT && (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR))) || + (yp_accepts_block_stack_p(parser) && match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_DO, YP_TOKEN_BRACE_LEFT)) + ) { + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + parse_arguments_list(parser, &arguments, true); + return (yp_node_t *) yp_call_node_fcall_create(parser, &constant, &arguments); + } + + yp_node_t *node = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); + + if ((binding_power == YP_BINDING_POWER_STATEMENT) && match_type_p(parser, YP_TOKEN_COMMA)) { + // If we get here, then we have a comma immediately following a + // constant, so we're going to parse this as a multiple assignment. + node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); + } + + return node; + } + case YP_TOKEN_UCOLON_COLON: { + parser_lex(parser); + + yp_token_t delimiter = parser->previous; + expect(parser, YP_TOKEN_CONSTANT, "Expected a constant after ::."); + + yp_node_t *constant = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); + yp_node_t *node = (yp_node_t *)yp_constant_path_node_create(parser, NULL, &delimiter, constant); + + if ((binding_power == YP_BINDING_POWER_STATEMENT) && match_type_p(parser, YP_TOKEN_COMMA)) { + node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); + } + + return node; + } + case YP_TOKEN_UDOT_DOT: + case YP_TOKEN_UDOT_DOT_DOT: { + yp_token_t operator = parser->current; + parser_lex(parser); + + yp_node_t *right = parse_expression(parser, binding_power, "Expected a value after the operator."); + return (yp_node_t *) yp_range_node_create(parser, NULL, &operator, right); + } + case YP_TOKEN_FLOAT: + parser_lex(parser); + return (yp_node_t *)yp_float_node_create(parser, &parser->previous); + case YP_TOKEN_NUMBERED_REFERENCE: { + parser_lex(parser); + yp_node_t *node = (yp_node_t *) yp_numbered_reference_read_node_create(parser, &parser->previous); + + if (binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { + node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); + } + + return node; + } + case YP_TOKEN_GLOBAL_VARIABLE: { + parser_lex(parser); + yp_node_t *node = (yp_node_t *) yp_global_variable_read_node_create(parser, &parser->previous); + + if (binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { + node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); + } + + return node; + } + case YP_TOKEN_BACK_REFERENCE: { + parser_lex(parser); + yp_node_t *node = (yp_node_t *) yp_back_reference_read_node_create(parser, &parser->previous); + + if (binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { + node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); + } + + return node; + } + case YP_TOKEN_IDENTIFIER: { + parser_lex(parser); + yp_token_t identifier = parser->previous; + yp_node_t *node = parse_vcall(parser); + + if (node->type == YP_NODE_CALL_NODE) { + // If parse_vcall returned with a call node, then we know the identifier + // is not in the local table. In that case we need to check if there are + // arguments following the identifier. + yp_call_node_t *call = (yp_call_node_t *) node; + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + parse_arguments_list(parser, &arguments, true); + + call->opening_loc = arguments.opening_loc; + call->arguments = arguments.arguments; + call->closing_loc = arguments.closing_loc; + call->block = arguments.block; + + if (arguments.block != NULL) { + call->base.location.end = arguments.block->base.location.end; + } else if (arguments.closing_loc.start == NULL) { + if (arguments.arguments != NULL) { + call->base.location.end = arguments.arguments->base.location.end; + } else { + call->base.location.end = call->message_loc.end; + } + } else { + call->base.location.end = arguments.closing_loc.end; + } + } else { + // Otherwise, we know the identifier is in the local table. This can + // still be a method call if it is followed by arguments or a block, so + // we need to check for that here. + if ( + (binding_power <= YP_BINDING_POWER_ASSIGNMENT && (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR))) || + (yp_accepts_block_stack_p(parser) && match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_DO, YP_TOKEN_BRACE_LEFT)) + ) { + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + parse_arguments_list(parser, &arguments, true); + + yp_call_node_t *fcall = yp_call_node_fcall_create(parser, &identifier, &arguments); + yp_node_destroy(parser, node); + return (yp_node_t *) fcall; + } + } + + if ((binding_power == YP_BINDING_POWER_STATEMENT) && match_type_p(parser, YP_TOKEN_COMMA)) { + node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); + } + + return node; + } + case YP_TOKEN_HEREDOC_START: { + assert(parser->lex_modes.current->mode == YP_LEX_HEREDOC); + yp_heredoc_quote_t quote = parser->lex_modes.current->as.heredoc.quote; + yp_heredoc_indent_t indent = parser->lex_modes.current->as.heredoc.indent; + + yp_node_t *node; + if (quote == YP_HEREDOC_QUOTE_BACKTICK) { + node = (yp_node_t *) yp_interpolated_xstring_node_create(parser, &parser->current, &parser->current); + } else { + node = (yp_node_t *) yp_interpolated_string_node_create(parser, &parser->current, NULL, &parser->current); + } + + parser_lex(parser); + yp_node_t *part; + + while (!match_any_type_p(parser, 2, YP_TOKEN_HEREDOC_END, YP_TOKEN_EOF)) { + if ((part = parse_string_part(parser)) == NULL) continue; + + if (quote == YP_HEREDOC_QUOTE_BACKTICK) { + yp_interpolated_xstring_node_append((yp_interpolated_x_string_node_t *) node, part); + } else { + yp_interpolated_string_node_append((yp_interpolated_string_node_t *) node, part); + } + } + + lex_state_set(parser, YP_LEX_STATE_END); + expect(parser, YP_TOKEN_HEREDOC_END, "Expected a closing delimiter for heredoc."); + if (quote == YP_HEREDOC_QUOTE_BACKTICK) { + assert(node->type == YP_NODE_INTERPOLATED_X_STRING_NODE); + yp_interpolated_xstring_node_closing_set(((yp_interpolated_x_string_node_t *) node), &parser->previous); + } else { + assert(node->type == YP_NODE_INTERPOLATED_STRING_NODE); + yp_interpolated_string_node_closing_set((yp_interpolated_string_node_t *) node, &parser->previous); + } + + // If this is a heredoc that is indented with a ~, then we need to dedent + // each line by the common leading whitespace. + if (indent == YP_HEREDOC_INDENT_TILDE) { + parse_heredoc_dedent(parser, node, quote); + } + + // If there's a string immediately following this heredoc, then it's a + // concatenatation. In this case we'll parse the next string and create a + // node in the tree that concatenates the two strings. + if (parser->current.type == YP_TOKEN_STRING_BEGIN) { + return (yp_node_t *) yp_string_concat_node_create( + parser, + node, + parse_expression(parser, YP_BINDING_POWER_CALL, "Expected string on the right side of concatenation.") + ); + } else { + return node; + } + } + case YP_TOKEN_IMAGINARY_NUMBER: + parser_lex(parser); + return (yp_node_t *) yp_imaginary_node_create(parser, &parser->previous); + case YP_TOKEN_INSTANCE_VARIABLE: { + parser_lex(parser); + yp_node_t *node = (yp_node_t *) yp_instance_variable_read_node_create(parser, &parser->previous); + + if (binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { + node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); + } + + return node; + } + case YP_TOKEN_INTEGER: + parser_lex(parser); + return (yp_node_t *) yp_integer_node_create(parser, &parser->previous); + case YP_TOKEN_KEYWORD___ENCODING__: + parser_lex(parser); + return (yp_node_t *) yp_source_encoding_node_create(parser, &parser->previous); + case YP_TOKEN_KEYWORD___FILE__: + parser_lex(parser); + return (yp_node_t *) yp_source_file_node_create(parser, &parser->previous); + case YP_TOKEN_KEYWORD___LINE__: + parser_lex(parser); + return (yp_node_t *) yp_source_line_node_create(parser, &parser->previous); + case YP_TOKEN_KEYWORD_ALIAS: { + parser_lex(parser); + yp_token_t keyword = parser->previous; + + yp_node_t *new_name = parse_alias_argument(parser, true); + yp_node_t *old_name = parse_alias_argument(parser, false); + + switch (new_name->type) { + case YP_NODE_SYMBOL_NODE: + case YP_NODE_INTERPOLATED_SYMBOL_NODE: { + if (old_name->type != YP_NODE_SYMBOL_NODE && old_name->type != YP_NODE_INTERPOLATED_SYMBOL_NODE) { + yp_diagnostic_list_append(&parser->error_list, old_name->location.start, old_name->location.end, "Expected a bare word or symbol argument."); + } + break; + } + case YP_NODE_BACK_REFERENCE_READ_NODE: + case YP_NODE_NUMBERED_REFERENCE_READ_NODE: + case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { + if (old_name->type == YP_NODE_BACK_REFERENCE_READ_NODE || old_name->type == YP_NODE_NUMBERED_REFERENCE_READ_NODE || old_name->type == YP_NODE_GLOBAL_VARIABLE_READ_NODE) { + if (old_name->type == YP_NODE_NUMBERED_REFERENCE_READ_NODE) { + yp_diagnostic_list_append(&parser->error_list, old_name->location.start, old_name->location.end, "Can't make alias for number variables."); + } + } else { + yp_diagnostic_list_append(&parser->error_list, old_name->location.start, old_name->location.end, "Expected a global variable."); + } + break; + } + default: + break; + } + + return (yp_node_t *) yp_alias_node_create(parser, &keyword, new_name, old_name); + } + case YP_TOKEN_KEYWORD_CASE: { + parser_lex(parser); + yp_token_t case_keyword = parser->previous; + yp_node_t *predicate = NULL; + + if ( + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON) || + match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_WHEN, YP_TOKEN_KEYWORD_IN, YP_TOKEN_KEYWORD_END) || + !token_begins_expression_p(parser->current.type) + ) { + predicate = NULL; + } else { + predicate = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected a value after case keyword."); + while (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)); + } + + if (accept(parser, YP_TOKEN_KEYWORD_END)) { + return (yp_node_t *) yp_case_node_create(parser, &case_keyword, predicate, NULL, &parser->previous); + } + + // At this point we can create a case node, though we don't yet know if it + // is a case-in or case-when node. + yp_token_t end_keyword = not_provided(parser); + yp_case_node_t *case_node = yp_case_node_create(parser, &case_keyword, predicate, NULL, &end_keyword); + + if (match_type_p(parser, YP_TOKEN_KEYWORD_WHEN)) { + // At this point we've seen a when keyword, so we know this is a + // case-when node. We will continue to parse the when nodes until we hit + // the end of the list. + while (accept(parser, YP_TOKEN_KEYWORD_WHEN)) { + yp_token_t when_keyword = parser->previous; + yp_when_node_t *when_node = yp_when_node_create(parser, &when_keyword); + + do { + if (accept(parser, YP_TOKEN_USTAR)) { + yp_token_t operator = parser->previous; + yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after `*' operator."); + + yp_splat_node_t *splat_node = yp_splat_node_create(parser, &operator, expression); + yp_when_node_conditions_append(when_node, (yp_node_t *) splat_node); + + if (expression->type == YP_NODE_MISSING_NODE) break; + } else { + yp_node_t *condition = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after when keyword."); + yp_when_node_conditions_append(when_node, condition); + + if (condition->type == YP_NODE_MISSING_NODE) break; + } + } while (accept(parser, YP_TOKEN_COMMA)); + + if (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { + accept(parser, YP_TOKEN_KEYWORD_THEN); + } else { + expect(parser, YP_TOKEN_KEYWORD_THEN, "Expected a delimiter after the predicates of a `when' clause."); + } + + if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_WHEN, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_KEYWORD_END)) { + yp_statements_node_t *statements = parse_statements(parser, YP_CONTEXT_CASE_WHEN); + if (statements != NULL) { + yp_when_node_statements_set(when_node, statements); + } + } + + yp_case_node_condition_append(case_node, (yp_node_t *) when_node); + } + } else { + // At this point we expect that we're parsing a case-in node. We will + // continue to parse the in nodes until we hit the end of the list. + while (match_type_p(parser, YP_TOKEN_KEYWORD_IN)) { + bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; + parser->pattern_matching_newlines = true; + + lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); + parser->command_start = false; + parser_lex(parser); + + yp_token_t in_keyword = parser->previous; + yp_node_t *pattern = parse_pattern(parser, true, "Expected a pattern after `in' keyword."); + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + + // Since we're in the top-level of the case-in node we need to check + // for guard clauses in the form of `if` or `unless` statements. + if (accept(parser, YP_TOKEN_KEYWORD_IF_MODIFIER)) { + yp_token_t keyword = parser->previous; + yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after guard keyword."); + pattern = (yp_node_t *) yp_if_node_modifier_create(parser, pattern, &keyword, predicate); + } else if (accept(parser, YP_TOKEN_KEYWORD_UNLESS_MODIFIER)) { + yp_token_t keyword = parser->previous; + yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after guard keyword."); + pattern = (yp_node_t *) yp_unless_node_modifier_create(parser, pattern, &keyword, predicate); + } + + // Now we need to check for the terminator of the in node's pattern. + // It can be a newline or semicolon optionally followed by a `then` + // keyword. + yp_token_t then_keyword; + if (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { + if (accept(parser, YP_TOKEN_KEYWORD_THEN)) { + then_keyword = parser->previous; + } else { + then_keyword = not_provided(parser); + } + } else { + expect(parser, YP_TOKEN_KEYWORD_THEN, "Expected a delimiter after the predicates of an `in' clause."); + then_keyword = parser->previous; + } + + // Now we can actually parse the statements associated with the in + // node. + yp_statements_node_t *statements; + if (match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_IN, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_KEYWORD_END)) { + statements = NULL; + } else { + statements = parse_statements(parser, YP_CONTEXT_CASE_IN); + } + + // Now that we have the full pattern and statements, we can create the + // node and attach it to the case node. + yp_node_t *condition = (yp_node_t *) yp_in_node_create(parser, pattern, statements, &in_keyword, &then_keyword); + yp_case_node_condition_append(case_node, condition); + } + } + + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + if (accept(parser, YP_TOKEN_KEYWORD_ELSE)) { + if (case_node->conditions.size < 1) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected else without no when clauses in case statement."); + } + + yp_token_t else_keyword = parser->previous; + yp_else_node_t *else_node; + + if (!match_type_p(parser, YP_TOKEN_KEYWORD_END)) { + else_node = yp_else_node_create(parser, &else_keyword, parse_statements(parser, YP_CONTEXT_ELSE), &parser->current); + } else { + else_node = yp_else_node_create(parser, &else_keyword, NULL, &parser->current); + } + + yp_case_node_consequent_set(case_node, else_node); + } + + expect(parser, YP_TOKEN_KEYWORD_END, "Expected case statement to end with an end keyword."); + yp_case_node_end_keyword_loc_set(case_node, &parser->previous); + return (yp_node_t *) case_node; + } + case YP_TOKEN_KEYWORD_BEGIN: { + parser_lex(parser); + + yp_token_t begin_keyword = parser->previous; + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + yp_statements_node_t *begin_statements = NULL; + + if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { + yp_accepts_block_stack_push(parser, true); + begin_statements = parse_statements(parser, YP_CONTEXT_BEGIN); + yp_accepts_block_stack_pop(parser); + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + } + + yp_begin_node_t *begin_node = yp_begin_node_create(parser, &begin_keyword, begin_statements); + parse_rescues(parser, begin_node); + + expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `begin` statement."); + begin_node->base.location.end = parser->previous.end; + yp_begin_node_end_keyword_set(begin_node, &parser->previous); + + if ((begin_node->else_clause != NULL) && (begin_node->rescue_clause == NULL)) { + yp_diagnostic_list_append( + &parser->error_list, + begin_node->else_clause->base.location.start, + begin_node->else_clause->base.location.end, + "else without rescue is useless" + ); + } + + return (yp_node_t *) begin_node; + } + case YP_TOKEN_KEYWORD_BEGIN_UPCASE: { + parser_lex(parser); + yp_token_t keyword = parser->previous; + + expect(parser, YP_TOKEN_BRACE_LEFT, "Expected '{' after 'BEGIN'."); + yp_token_t opening = parser->previous; + yp_statements_node_t *statements = parse_statements(parser, YP_CONTEXT_PREEXE); + + expect(parser, YP_TOKEN_BRACE_RIGHT, "Expected '}' after 'BEGIN' statements."); + return (yp_node_t *) yp_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous); + } + case YP_TOKEN_KEYWORD_BREAK: + case YP_TOKEN_KEYWORD_NEXT: + case YP_TOKEN_KEYWORD_RETURN: { + parser_lex(parser); + + yp_token_t keyword = parser->previous; + yp_arguments_node_t *arguments = NULL; + + if ( + token_begins_expression_p(parser->current.type) || + match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR) + ) { + yp_binding_power_t binding_power = yp_binding_powers[parser->current.type].left; + + if (binding_power == YP_BINDING_POWER_UNSET || binding_power >= YP_BINDING_POWER_RANGE) { + arguments = yp_arguments_node_create(parser); + parse_arguments(parser, arguments, false, YP_TOKEN_EOF); + } + } + + switch (keyword.type) { + case YP_TOKEN_KEYWORD_BREAK: + return (yp_node_t *) yp_break_node_create(parser, &keyword, arguments); + case YP_TOKEN_KEYWORD_NEXT: + return (yp_node_t *) yp_next_node_create(parser, &keyword, arguments); + case YP_TOKEN_KEYWORD_RETURN: { + if ( + (parser->current_context->context == YP_CONTEXT_CLASS) || + (parser->current_context->context == YP_CONTEXT_MODULE) + ) { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid return in class/module body"); + } + return (yp_node_t *) yp_return_node_create(parser, &keyword, arguments); + } + default: + assert(false && "unreachable"); + return (yp_node_t *) yp_missing_node_create(parser, parser->previous.start, parser->previous.end); + } + } + case YP_TOKEN_KEYWORD_SUPER: { + parser_lex(parser); + + yp_token_t keyword = parser->previous; + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + parse_arguments_list(parser, &arguments, true); + + if (arguments.opening_loc.start == NULL && arguments.arguments == NULL) { + return (yp_node_t *) yp_forwarding_super_node_create(parser, &keyword, &arguments); + } + + return (yp_node_t *) yp_super_node_create(parser, &keyword, &arguments); + } + case YP_TOKEN_KEYWORD_YIELD: { + parser_lex(parser); + + yp_token_t keyword = parser->previous; + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + parse_arguments_list(parser, &arguments, false); + + return (yp_node_t *) yp_yield_node_create(parser, &keyword, &arguments.opening_loc, arguments.arguments, &arguments.closing_loc); + } + case YP_TOKEN_KEYWORD_CLASS: { + parser_lex(parser); + yp_token_t class_keyword = parser->previous; + yp_do_loop_stack_push(parser, false); + + if (accept(parser, YP_TOKEN_LESS_LESS)) { + yp_token_t operator = parser->previous; + yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_NOT, "Expected to find an expression after `<<`."); + + yp_parser_scope_push(parser, true); + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + + yp_node_t *statements = NULL; + if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { + statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_SCLASS); + } + + if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { + assert(statements == NULL || statements->type == YP_NODE_STATEMENTS_NODE); + statements = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) statements); + } + + expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `class` statement."); + + yp_constant_id_list_t locals = parser->current_scope->locals; + yp_parser_scope_pop(parser); + yp_do_loop_stack_pop(parser); + return (yp_node_t *) yp_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous); + } + + yp_node_t *name = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected to find a class name after `class`."); + yp_token_t inheritance_operator; + yp_node_t *superclass; + + if (match_type_p(parser, YP_TOKEN_LESS)) { + inheritance_operator = parser->current; + lex_state_set(parser, YP_LEX_STATE_BEG); + + parser->command_start = true; + parser_lex(parser); + + superclass = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected to find a superclass after `<`."); + } else { + inheritance_operator = not_provided(parser); + superclass = NULL; + } + + yp_parser_scope_push(parser, true); + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + yp_node_t *statements = NULL; + + if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { + yp_accepts_block_stack_push(parser, true); + statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_CLASS); + yp_accepts_block_stack_pop(parser); + } + + if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { + assert(statements == NULL || statements->type == YP_NODE_STATEMENTS_NODE); + statements = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) statements); + } + + expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `class` statement."); + + if (context_def_p(parser)) { + yp_diagnostic_list_append(&parser->error_list, class_keyword.start, class_keyword.end, "Class definition in method body"); + } + + yp_constant_id_list_t locals = parser->current_scope->locals; + yp_parser_scope_pop(parser); + yp_do_loop_stack_pop(parser); + return (yp_node_t *) yp_class_node_create(parser, &locals, &class_keyword, name, &inheritance_operator, superclass, statements, &parser->previous); + } + case YP_TOKEN_KEYWORD_DEF: { + yp_token_t def_keyword = parser->current; + + yp_node_t *receiver = NULL; + yp_token_t operator = not_provided(parser); + yp_token_t name = not_provided(parser); + + context_push(parser, YP_CONTEXT_DEF_PARAMS); + parser_lex(parser); + + switch (parser->current.type) { + case YP_CASE_OPERATOR: + yp_parser_scope_push(parser, true); + lex_state_set(parser, YP_LEX_STATE_ENDFN); + parser_lex(parser); + name = parser->previous; + break; + case YP_TOKEN_IDENTIFIER: { + yp_parser_scope_push(parser, true); + parser_lex(parser); + + if (match_any_type_p(parser, 2, YP_TOKEN_DOT, YP_TOKEN_COLON_COLON)) { + receiver = parse_vcall(parser); + + lex_state_set(parser, YP_LEX_STATE_FNAME); + parser_lex(parser); + + operator = parser->previous; + name = parse_method_definition_name(parser); + + if (name.type == YP_TOKEN_MISSING) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Expected a method name after receiver."); + } + } else { + name = parser->previous; + } + + break; + } + case YP_TOKEN_CONSTANT: + case YP_TOKEN_INSTANCE_VARIABLE: + case YP_TOKEN_CLASS_VARIABLE: + case YP_TOKEN_GLOBAL_VARIABLE: + case YP_TOKEN_KEYWORD_NIL: + case YP_TOKEN_KEYWORD_SELF: + case YP_TOKEN_KEYWORD_TRUE: + case YP_TOKEN_KEYWORD_FALSE: + case YP_TOKEN_KEYWORD___FILE__: + case YP_TOKEN_KEYWORD___LINE__: + case YP_TOKEN_KEYWORD___ENCODING__: { + yp_parser_scope_push(parser, true); + parser_lex(parser); + yp_token_t identifier = parser->previous; + + if (match_any_type_p(parser, 2, YP_TOKEN_DOT, YP_TOKEN_COLON_COLON)) { + lex_state_set(parser, YP_LEX_STATE_FNAME); + parser_lex(parser); + operator = parser->previous; + + switch (identifier.type) { + case YP_TOKEN_CONSTANT: + receiver = (yp_node_t *) yp_constant_read_node_create(parser, &identifier); + break; + case YP_TOKEN_INSTANCE_VARIABLE: + receiver = (yp_node_t *) yp_instance_variable_read_node_create(parser, &identifier); + break; + case YP_TOKEN_CLASS_VARIABLE: + receiver = (yp_node_t *) yp_class_variable_read_node_create(parser, &identifier); + break; + case YP_TOKEN_GLOBAL_VARIABLE: + receiver = (yp_node_t *) yp_global_variable_read_node_create(parser, &identifier); + break; + case YP_TOKEN_KEYWORD_NIL: + receiver = (yp_node_t *) yp_nil_node_create(parser, &identifier); + break; + case YP_TOKEN_KEYWORD_SELF: + receiver = (yp_node_t *) yp_self_node_create(parser, &identifier); + break; + case YP_TOKEN_KEYWORD_TRUE: + receiver = (yp_node_t *) yp_true_node_create(parser, &identifier); + break; + case YP_TOKEN_KEYWORD_FALSE: + receiver = (yp_node_t *)yp_false_node_create(parser, &identifier); + break; + case YP_TOKEN_KEYWORD___FILE__: + receiver = (yp_node_t *) yp_source_file_node_create(parser, &identifier); + break; + case YP_TOKEN_KEYWORD___LINE__: + receiver = (yp_node_t *) yp_source_line_node_create(parser, &identifier); + break; + case YP_TOKEN_KEYWORD___ENCODING__: + receiver = (yp_node_t *) yp_source_encoding_node_create(parser, &identifier); + break; + default: + break; + } + + name = parse_method_definition_name(parser); + if (name.type == YP_TOKEN_MISSING) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Expected a method name after receiver."); + } + } else { + name = identifier; + } + break; + } + case YP_TOKEN_PARENTHESIS_LEFT: { + parser_lex(parser); + yp_token_t lparen = parser->previous; + yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected to be able to parse receiver."); + + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected closing ')' for receiver."); + yp_token_t rparen = parser->previous; + + lex_state_set(parser, YP_LEX_STATE_FNAME); + expect_any(parser, "Expected '.' or '::' after receiver", 2, YP_TOKEN_DOT, YP_TOKEN_COLON_COLON); + + operator = parser->previous; + receiver = (yp_node_t *) yp_parentheses_node_create(parser, &lparen, expression, &rparen); + + yp_parser_scope_push(parser, true); + name = parse_method_definition_name(parser); + break; + } + default: + yp_parser_scope_push(parser, true); + name = parse_method_definition_name(parser); + + if (name.type == YP_TOKEN_MISSING) { + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Expected a method name after receiver."); + } + break; + } + + yp_token_t lparen; + yp_token_t rparen; + yp_parameters_node_t *params; + + switch (parser->current.type) { + case YP_TOKEN_PARENTHESIS_LEFT: { + parser_lex(parser); + lparen = parser->previous; + + if (match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { + params = NULL; + } else { + params = parse_parameters(parser, YP_BINDING_POWER_DEFINED, true, false, true); + } + + lex_state_set(parser, YP_LEX_STATE_BEG); + parser->command_start = true; + + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' after left parenthesis."); + rparen = parser->previous; + break; + } + case YP_CASE_PARAMETER: { + lparen = not_provided(parser); + rparen = not_provided(parser); + params = parse_parameters(parser, YP_BINDING_POWER_DEFINED, false, false, true); + break; + } + default: { + lparen = not_provided(parser); + rparen = not_provided(parser); + params = NULL; + break; + } + } + + context_pop(parser); + yp_node_t *statements = NULL; + yp_token_t equal; + yp_token_t end_keyword; + + if (accept(parser, YP_TOKEN_EQUAL)) { + if (token_is_setter_name(&name)) { + yp_diagnostic_list_append(&parser->error_list, name.start, name.end, "Setter method cannot be defined in an endless method definition"); + } + equal = parser->previous; + + context_push(parser, YP_CONTEXT_DEF); + statements = (yp_node_t *) yp_statements_node_create(parser); + + yp_node_t *statement = parse_expression(parser, YP_BINDING_POWER_ASSIGNMENT + 1, "Expected to be able to parse body of endless method definition."); + + if (accept(parser, YP_TOKEN_KEYWORD_RESCUE_MODIFIER)) { + yp_token_t rescue_keyword = parser->previous; + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the rescue keyword."); + yp_rescue_modifier_node_t *rescue_node = yp_rescue_modifier_node_create(parser, statement, &rescue_keyword, value); + statement = (yp_node_t *)rescue_node; + } + + yp_statements_node_body_append((yp_statements_node_t *) statements, statement); + context_pop(parser); + end_keyword = not_provided(parser); + } else { + equal = not_provided(parser); + + if (lparen.type == YP_TOKEN_NOT_PROVIDED) { + lex_state_set(parser, YP_LEX_STATE_BEG); + parser->command_start = true; + expect_any(parser, "Expected a terminator after the parameters", 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + } else { + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + } + + yp_accepts_block_stack_push(parser, true); + yp_do_loop_stack_push(parser, false); + + if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { + statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_DEF); + } + + if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { + assert(statements == NULL || statements->type == YP_NODE_STATEMENTS_NODE); + statements = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) statements); + } + + yp_accepts_block_stack_pop(parser); + yp_do_loop_stack_pop(parser); + expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `def` statement."); + end_keyword = parser->previous; + } + + yp_constant_id_list_t locals = parser->current_scope->locals; + yp_parser_scope_pop(parser); + + return (yp_node_t *) yp_def_node_create( + parser, + &name, + receiver, + params, + statements, + &locals, + &def_keyword, + &operator, + &lparen, + &rparen, + &equal, + &end_keyword + ); + } + case YP_TOKEN_KEYWORD_DEFINED: { + parser_lex(parser); + yp_token_t keyword = parser->previous; + + yp_token_t lparen; + yp_token_t rparen; + yp_node_t *expression; + + if (accept(parser, YP_TOKEN_PARENTHESIS_LEFT)) { + lparen = parser->previous; + expression = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected expression after `defined?`."); + + if (parser->recovering) { + rparen = not_provided(parser); + } else { + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' after 'defined?' expression."); + rparen = parser->previous; + } + } else { + lparen = not_provided(parser); + rparen = not_provided(parser); + expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected expression after `defined?`."); + } + + return (yp_node_t *) yp_defined_node_create( + parser, + &lparen, + expression, + &rparen, + &(yp_location_t) { .start = keyword.start, .end = keyword.end } + ); + } + case YP_TOKEN_KEYWORD_END_UPCASE: { + parser_lex(parser); + yp_token_t keyword = parser->previous; + + expect(parser, YP_TOKEN_BRACE_LEFT, "Expected '{' after 'END'."); + yp_token_t opening = parser->previous; + yp_statements_node_t *statements = parse_statements(parser, YP_CONTEXT_POSTEXE); + + expect(parser, YP_TOKEN_BRACE_RIGHT, "Expected '}' after 'END' statements."); + return (yp_node_t *) yp_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous); + } + case YP_TOKEN_KEYWORD_FALSE: + parser_lex(parser); + return (yp_node_t *)yp_false_node_create(parser, &parser->previous); + case YP_TOKEN_KEYWORD_FOR: { + parser_lex(parser); + yp_token_t for_keyword = parser->previous; + + yp_node_t *index = parse_targets(parser, NULL, YP_BINDING_POWER_INDEX); + yp_do_loop_stack_push(parser, true); + + expect(parser, YP_TOKEN_KEYWORD_IN, "Expected keyword in."); + yp_token_t in_keyword = parser->previous; + + yp_node_t *collection = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected collection."); + yp_do_loop_stack_pop(parser); + + yp_token_t do_keyword; + if (accept(parser, YP_TOKEN_KEYWORD_DO_LOOP)) { + do_keyword = parser->previous; + } else { + do_keyword = not_provided(parser); + } + + accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE); + yp_statements_node_t *statements = NULL; + + if (!accept(parser, YP_TOKEN_KEYWORD_END)) { + statements = parse_statements(parser, YP_CONTEXT_FOR); + expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close for loop."); + } + + return (yp_node_t *) yp_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, &do_keyword, &parser->previous); + } + case YP_TOKEN_KEYWORD_IF: + parser_lex(parser); + return parse_conditional(parser, YP_CONTEXT_IF); + case YP_TOKEN_KEYWORD_UNDEF: { + parser_lex(parser); + yp_undef_node_t *undef = yp_undef_node_create(parser, &parser->previous); + yp_node_t *name = parse_undef_argument(parser); + + if (name->type != YP_NODE_MISSING_NODE) { + yp_undef_node_append(undef, name); + + while (match_type_p(parser, YP_TOKEN_COMMA)) { + lex_state_set(parser, YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM); + parser_lex(parser); + name = parse_undef_argument(parser); + if (name->type == YP_NODE_MISSING_NODE) break; + + yp_undef_node_append(undef, name); + } + } else { + yp_node_destroy(parser, name); + } + + return (yp_node_t *) undef; + } + case YP_TOKEN_KEYWORD_NOT: { + parser_lex(parser); + + yp_token_t message = parser->previous; + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + yp_node_t *receiver = NULL; + + accept(parser, YP_TOKEN_NEWLINE); + + if (accept(parser, YP_TOKEN_PARENTHESIS_LEFT)) { + arguments.opening_loc = ((yp_location_t) { .start = parser->previous.start, .end = parser->previous.end }); + + if (accept(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { + arguments.closing_loc = ((yp_location_t) { .start = parser->previous.start, .end = parser->previous.end }); + } else { + receiver = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected expression after `not`."); + + if (!parser->recovering) { + accept(parser, YP_TOKEN_NEWLINE); + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' after 'not' expression."); + arguments.closing_loc = ((yp_location_t) { .start = parser->previous.start, .end = parser->previous.end }); + } + } + } else { + receiver = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected expression after `not`."); + } + + return (yp_node_t *) yp_call_node_not_create(parser, receiver, &message, &arguments); + } + case YP_TOKEN_KEYWORD_UNLESS: + parser_lex(parser); + return parse_conditional(parser, YP_CONTEXT_UNLESS); + case YP_TOKEN_KEYWORD_MODULE: { + parser_lex(parser); + + yp_token_t module_keyword = parser->previous; + yp_node_t *name = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected to find a module name after `module`."); + + // If we can recover from a syntax error that occurred while parsing the + // name of the module, then we'll handle that here. + if (name->type == YP_NODE_MISSING_NODE) { + yp_token_t end_keyword = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + return (yp_node_t *) yp_module_node_create(parser, NULL, &module_keyword, name, NULL, &end_keyword); + } + + while (accept(parser, YP_TOKEN_COLON_COLON)) { + yp_token_t double_colon = parser->previous; + + expect(parser, YP_TOKEN_CONSTANT, "Expected to find a module name after `::`."); + yp_node_t *constant = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); + + name = (yp_node_t *)yp_constant_path_node_create(parser, name, &double_colon, constant); + } + + yp_parser_scope_push(parser, true); + accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE); + yp_node_t *statements = NULL; + + if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { + yp_accepts_block_stack_push(parser, true); + statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_MODULE); + yp_accepts_block_stack_pop(parser); + } + + if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { + assert(statements == NULL || statements->type == YP_NODE_STATEMENTS_NODE); + statements = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) statements); + } + + yp_constant_id_list_t locals = parser->current_scope->locals; + yp_parser_scope_pop(parser); + + expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `module` statement."); + + if (context_def_p(parser)) { + yp_diagnostic_list_append(&parser->error_list, module_keyword.start, module_keyword.end, "Module definition in method body"); + } + + return (yp_node_t *) yp_module_node_create(parser, &locals, &module_keyword, name, statements, &parser->previous); + } + case YP_TOKEN_KEYWORD_NIL: + parser_lex(parser); + return (yp_node_t *) yp_nil_node_create(parser, &parser->previous); + case YP_TOKEN_KEYWORD_REDO: + parser_lex(parser); + return (yp_node_t *) yp_redo_node_create(parser, &parser->previous); + case YP_TOKEN_KEYWORD_RETRY: + parser_lex(parser); + return (yp_node_t *) yp_retry_node_create(parser, &parser->previous); + case YP_TOKEN_KEYWORD_SELF: + parser_lex(parser); + return (yp_node_t *) yp_self_node_create(parser, &parser->previous); + case YP_TOKEN_KEYWORD_TRUE: + parser_lex(parser); + return (yp_node_t *) yp_true_node_create(parser, &parser->previous); + case YP_TOKEN_KEYWORD_UNTIL: { + yp_do_loop_stack_push(parser, true); + parser_lex(parser); + yp_token_t keyword = parser->previous; + + yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected predicate expression after `until`."); + yp_do_loop_stack_pop(parser); + + accept_any(parser, 3, YP_TOKEN_KEYWORD_DO_LOOP, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + yp_statements_node_t *statements = NULL; + + if (!accept(parser, YP_TOKEN_KEYWORD_END)) { + yp_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, YP_CONTEXT_UNTIL); + yp_accepts_block_stack_pop(parser); + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `until` statement."); + } + + yp_until_node_t *until_node = yp_until_node_create(parser, &keyword, predicate, statements); + if (parser->previous.type == YP_TOKEN_KEYWORD_END) { + until_node->base.location.end = parser->previous.end; + } + + return (yp_node_t *) until_node; + } + case YP_TOKEN_KEYWORD_WHILE: { + yp_do_loop_stack_push(parser, true); + parser_lex(parser); + yp_token_t keyword = parser->previous; + + yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected predicate expression after `while`."); + yp_do_loop_stack_pop(parser); + + accept_any(parser, 3, YP_TOKEN_KEYWORD_DO_LOOP, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + yp_statements_node_t *statements = NULL; + + if (!accept(parser, YP_TOKEN_KEYWORD_END)) { + yp_accepts_block_stack_push(parser, true); + statements = parse_statements(parser, YP_CONTEXT_WHILE); + yp_accepts_block_stack_pop(parser); + accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); + expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `while` statement."); + } + + yp_while_node_t *while_node = yp_while_node_create(parser, &keyword, predicate, statements); + if (parser->previous.type == YP_TOKEN_KEYWORD_END) { + while_node->base.location.end = parser->previous.end; + } + return (yp_node_t *) while_node; + } + case YP_TOKEN_PERCENT_LOWER_I: { + parser_lex(parser); + yp_array_node_t *array = yp_array_node_create(parser, &parser->previous); + + while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { + if (yp_array_node_size(array) == 0) { + accept(parser, YP_TOKEN_WORDS_SEP); + } else { + expect(parser, YP_TOKEN_WORDS_SEP, "Expected a separator for the symbols in a `%i` list."); + if (match_type_p(parser, YP_TOKEN_STRING_END)) break; + } + + if (match_type_p(parser, YP_TOKEN_STRING_END)) break; + expect(parser, YP_TOKEN_STRING_CONTENT, "Expected a symbol in a `%i` list."); + + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + + yp_node_t *symbol = (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &parser->previous, &closing, YP_UNESCAPE_MINIMAL); + yp_array_node_elements_append(array, symbol); + } + + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a `%i` list."); + yp_array_node_close_set(array, &parser->previous); + + return (yp_node_t *) array; + } + case YP_TOKEN_PERCENT_UPPER_I: { + parser_lex(parser); + yp_array_node_t *array = yp_array_node_create(parser, &parser->previous); + + // This is the current node that we are parsing that will be added to the + // list of elements. + yp_node_t *current = NULL; + + while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { + switch (parser->current.type) { + case YP_TOKEN_WORDS_SEP: { + if (current == NULL) { + // If we hit a separator before we have any content, then we don't + // need to do anything. + } else { + // If we hit a separator after we've hit content, then we need to + // append that content to the list and reset the current node. + yp_array_node_elements_append(array, current); + current = NULL; + } + + parser_lex(parser); + break; + } + case YP_TOKEN_STRING_CONTENT: { + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + + if (current == NULL) { + // If we hit content and the current node is NULL, then this is + // the first string content we've seen. In that case we're going + // to create a new string node and set that to the current. + parser_lex(parser); + current = (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &parser->previous, &closing, YP_UNESCAPE_ALL); + } else if (current->type == YP_NODE_INTERPOLATED_SYMBOL_NODE) { + // If we hit string content and the current node is an + // interpolated string, then we need to append the string content + // to the list of child nodes. + yp_node_t *part = parse_string_part(parser); + yp_interpolated_symbol_node_append((yp_interpolated_symbol_node_t *) current, part); + } else { + assert(false && "unreachable"); + } + + break; + } + case YP_TOKEN_EMBVAR: { + bool start_location_set = false; + if (current == NULL) { + // If we hit an embedded variable and the current node is NULL, + // then this is the start of a new string. We'll set the current + // node to a new interpolated string. + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + current = (yp_node_t *) yp_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + } else if (current->type == YP_NODE_SYMBOL_NODE) { + // If we hit an embedded variable and the current node is a string + // node, then we'll convert the current into an interpolated + // string and add the string node to the list of parts. + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + yp_interpolated_symbol_node_t *interpolated = yp_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + + current = (yp_node_t *) yp_symbol_node_to_string_node(parser, (yp_symbol_node_t *) current); + yp_interpolated_symbol_node_append(interpolated, current); + interpolated->base.location.start = current->location.start; + start_location_set = true; + current = (yp_node_t *) interpolated; + } else { + // If we hit an embedded variable and the current node is an + // interpolated string, then we'll just add the embedded variable. + } + + yp_node_t *part = parse_string_part(parser); + yp_interpolated_symbol_node_append((yp_interpolated_symbol_node_t *) current, part); + if (!start_location_set) { + current->location.start = part->location.start; + } + break; + } + case YP_TOKEN_EMBEXPR_BEGIN: { + bool start_location_set = false; + if (current == NULL) { + // If we hit an embedded expression and the current node is NULL, + // then this is the start of a new string. We'll set the current + // node to a new interpolated string. + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + current = (yp_node_t *) yp_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + } else if (current->type == YP_NODE_SYMBOL_NODE) { + // If we hit an embedded expression and the current node is a + // string node, then we'll convert the current into an + // interpolated string and add the string node to the list of + // parts. + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + yp_interpolated_symbol_node_t *interpolated = yp_interpolated_symbol_node_create(parser, &opening, NULL, &closing); + + current = (yp_node_t *) yp_symbol_node_to_string_node(parser, (yp_symbol_node_t *) current); + yp_interpolated_symbol_node_append(interpolated, current); + interpolated->base.location.start = current->location.start; + start_location_set = true; + current = (yp_node_t *) interpolated; + } else if (current->type == YP_NODE_INTERPOLATED_SYMBOL_NODE) { + // If we hit an embedded expression and the current node is an + // interpolated string, then we'll just continue on. + } else { + assert(false && "unreachable"); + } + + yp_node_t *part = parse_string_part(parser); + yp_interpolated_symbol_node_append((yp_interpolated_symbol_node_t *) current, part); + if (!start_location_set) { + current->location.start = part->location.start; + } + break; + } + default: + expect(parser, YP_TOKEN_STRING_CONTENT, "Expected a symbol in a `%I` list."); + parser_lex(parser); + break; + } + } + + // If we have a current node, then we need to append it to the list. + if (current) { + yp_array_node_elements_append(array, current); + } + + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a `%I` list."); + yp_array_node_close_set(array, &parser->previous); + + return (yp_node_t *) array; + } + case YP_TOKEN_PERCENT_LOWER_W: { + parser_lex(parser); + yp_array_node_t *array = yp_array_node_create(parser, &parser->previous); + + // skip all leading whitespaces + accept(parser, YP_TOKEN_WORDS_SEP); + + while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { + if (yp_array_node_size(array) == 0) { + accept(parser, YP_TOKEN_WORDS_SEP); + } else { + expect(parser, YP_TOKEN_WORDS_SEP, "Expected a separator for the strings in a `%w` list."); + if (match_type_p(parser, YP_TOKEN_STRING_END)) break; + } + expect(parser, YP_TOKEN_STRING_CONTENT, "Expected a string in a `%w` list."); + + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + yp_node_t *string = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &parser->previous, &closing, YP_UNESCAPE_MINIMAL); + yp_array_node_elements_append(array, string); + } + + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a `%w` list."); + yp_array_node_close_set(array, &parser->previous); + + return (yp_node_t *) array; + } + case YP_TOKEN_PERCENT_UPPER_W: { + parser_lex(parser); + yp_array_node_t *array = yp_array_node_create(parser, &parser->previous); + + // This is the current node that we are parsing that will be added to the + // list of elements. + yp_node_t *current = NULL; + + while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { + switch (parser->current.type) { + case YP_TOKEN_WORDS_SEP: { + if (current == NULL) { + // If we hit a separator before we have any content, then we don't + // need to do anything. + } else { + // If we hit a separator after we've hit content, then we need to + // append that content to the list and reset the current node. + yp_array_node_elements_append(array, current); + current = NULL; + } + + parser_lex(parser); + break; + } + case YP_TOKEN_STRING_CONTENT: { + if (current == NULL) { + // If we hit content and the current node is NULL, then this is + // the first string content we've seen. In that case we're going + // to create a new string node and set that to the current. + current = parse_string_part(parser); + } else if (current->type == YP_NODE_INTERPOLATED_STRING_NODE) { + // If we hit string content and the current node is an + // interpolated string, then we need to append the string content + // to the list of child nodes. + yp_node_t *part = parse_string_part(parser); + yp_interpolated_string_node_append((yp_interpolated_string_node_t *) current, part); + } else { + assert(false && "unreachable"); + } + + break; + } + case YP_TOKEN_EMBVAR: { + if (current == NULL) { + // If we hit an embedded variable and the current node is NULL, + // then this is the start of a new string. We'll set the current + // node to a new interpolated string. + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + current = (yp_node_t *) yp_interpolated_string_node_create(parser, &opening, NULL, &closing); + } else if (current->type == YP_NODE_STRING_NODE) { + // If we hit an embedded variable and the current node is a string + // node, then we'll convert the current into an interpolated + // string and add the string node to the list of parts. + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + yp_interpolated_string_node_t *interpolated = yp_interpolated_string_node_create(parser, &opening, NULL, &closing); + yp_interpolated_string_node_append(interpolated, current); + current = (yp_node_t *) interpolated; + } else { + // If we hit an embedded variable and the current node is an + // interpolated string, then we'll just add the embedded variable. + } + + yp_node_t *part = parse_string_part(parser); + yp_interpolated_string_node_append((yp_interpolated_string_node_t *) current, part); + break; + } + case YP_TOKEN_EMBEXPR_BEGIN: { + if (current == NULL) { + // If we hit an embedded expression and the current node is NULL, + // then this is the start of a new string. We'll set the current + // node to a new interpolated string. + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + current = (yp_node_t *) yp_interpolated_string_node_create(parser, &opening, NULL, &closing); + } else if (current->type == YP_NODE_STRING_NODE) { + // If we hit an embedded expression and the current node is a + // string node, then we'll convert the current into an + // interpolated string and add the string node to the list of + // parts. + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + yp_interpolated_string_node_t *interpolated = yp_interpolated_string_node_create(parser, &opening, NULL, &closing); + yp_interpolated_string_node_append(interpolated, current); + current = (yp_node_t *) interpolated; + } else if (current->type == YP_NODE_INTERPOLATED_STRING_NODE) { + // If we hit an embedded expression and the current node is an + // interpolated string, then we'll just continue on. + } else { + assert(false && "unreachable"); + } + + yp_node_t *part = parse_string_part(parser); + yp_interpolated_string_node_append((yp_interpolated_string_node_t *) current, part); + break; + } + default: + expect(parser, YP_TOKEN_STRING_CONTENT, "Expected a string in a `%W` list."); + parser_lex(parser); + break; + } + } + + // If we have a current node, then we need to append it to the list. + if (current) { + yp_array_node_elements_append(array, current); + } + + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a `%W` list."); + yp_array_node_close_set(array, &parser->previous); + + return (yp_node_t *) array; + } + case YP_TOKEN_RATIONAL_NUMBER: + parser_lex(parser); + return (yp_node_t *) yp_rational_node_create(parser, &parser->previous); + case YP_TOKEN_REGEXP_BEGIN: { + yp_token_t opening = parser->current; + parser_lex(parser); + + if (match_type_p(parser, YP_TOKEN_REGEXP_END)) { + // If we get here, then we have an end immediately after a start. In + // that case we'll create an empty content token and return an + // uninterpolated regular expression. + yp_token_t content = (yp_token_t) { + .type = YP_TOKEN_STRING_CONTENT, + .start = parser->previous.end, + .end = parser->previous.end + }; + + parser_lex(parser); + return (yp_node_t *) yp_regular_expression_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_ALL); + } + + yp_interpolated_regular_expression_node_t *node; + + if (match_type_p(parser, YP_TOKEN_STRING_CONTENT)) { + // In this case we've hit string content so we know the regular + // expression at least has something in it. We'll need to check if the + // following token is the end (in which case we can return a plain + // regular expression) or if it's not then it has interpolation. + yp_token_t content = parser->current; + parser_lex(parser); + + // If we hit an end, then we can create a regular expression node + // without interpolation, which can be represented more succinctly and + // more easily compiled. + if (accept(parser, YP_TOKEN_REGEXP_END)) { + return (yp_node_t *) yp_regular_expression_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_ALL); + } + + // If we get here, then we have interpolation so we'll need to create + // a regular expression node with interpolation. + node = yp_interpolated_regular_expression_node_create(parser, &opening); + + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + yp_node_t *part = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &parser->previous, &closing, YP_UNESCAPE_ALL); + yp_interpolated_regular_expression_node_append(node, part); + } else { + // If the first part of the body of the regular expression is not a + // string content, then we have interpolation and we need to create an + // interpolated regular expression node. + node = yp_interpolated_regular_expression_node_create(parser, &opening); + } + + // Now that we're here and we have interpolation, we'll parse all of the + // parts into the list. + while (!match_any_type_p(parser, 2, YP_TOKEN_REGEXP_END, YP_TOKEN_EOF)) { + yp_node_t *part = parse_string_part(parser); + if (part != NULL) { + yp_interpolated_regular_expression_node_append(node, part); + } + } + + expect(parser, YP_TOKEN_REGEXP_END, "Expected a closing delimiter for a regular expression."); + yp_interpolated_regular_expression_node_closing_set(node, &parser->previous); + + return (yp_node_t *) node; + } + case YP_TOKEN_BACKTICK: + case YP_TOKEN_PERCENT_LOWER_X: { + parser_lex(parser); + yp_token_t opening = parser->previous; + + // When we get here, we don't know if this string is going to have + // interpolation or not, even though it is allowed. Still, we want to be + // able to return a string node without interpolation if we can since + // it'll be faster. + if (match_type_p(parser, YP_TOKEN_STRING_END)) { + // If we get here, then we have an end immediately after a start. In + // that case we'll create an empty content token and return an + // uninterpolated string. + yp_token_t content = (yp_token_t) { + .type = YP_TOKEN_STRING_CONTENT, + .start = parser->previous.end, + .end = parser->previous.end + }; + + parser_lex(parser); + return (yp_node_t *) yp_xstring_node_create(parser, &opening, &content, &parser->previous); + } + + yp_interpolated_x_string_node_t *node; + + if (match_type_p(parser, YP_TOKEN_STRING_CONTENT)) { + // In this case we've hit string content so we know the string at least + // has something in it. We'll need to check if the following token is + // the end (in which case we can return a plain string) or if it's not + // then it has interpolation. + yp_token_t content = parser->current; + parser_lex(parser); + + if (accept(parser, YP_TOKEN_STRING_END)) { + return (yp_node_t *) yp_xstring_node_create_and_unescape(parser, &opening, &content, &parser->previous); + } + + // If we get here, then we have interpolation so we'll need to create + // a string node with interpolation. + node = yp_interpolated_xstring_node_create(parser, &opening, &opening); + + yp_token_t opening = not_provided(parser); + yp_token_t closing = not_provided(parser); + yp_node_t *part = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &parser->previous, &closing, YP_UNESCAPE_ALL); + yp_interpolated_xstring_node_append(node, part); + } else { + // If the first part of the body of the string is not a string content, + // then we have interpolation and we need to create an interpolated + // string node. + node = yp_interpolated_xstring_node_create(parser, &opening, &opening); + } + + while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { + yp_node_t *part = parse_string_part(parser); + if (part != NULL) { + yp_interpolated_xstring_node_append(node, part); + } + } + + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for an xstring."); + yp_interpolated_xstring_node_closing_set(node, &parser->previous); + return (yp_node_t *) node; + } + case YP_TOKEN_USTAR: { + parser_lex(parser); + + // * operators at the beginning of expressions are only valid in the + // context of a multiple assignment. We enforce that here. We'll still lex + // past it though and create a missing node place. + if (binding_power != YP_BINDING_POWER_STATEMENT) { + return (yp_node_t *) yp_missing_node_create(parser, parser->previous.start, parser->previous.end); + } + + yp_token_t operator = parser->previous; + yp_node_t *name = NULL; + + if (token_begins_expression_p(parser->current.type)) { + name = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected an expression after '*'."); + } + + yp_node_t *splat = (yp_node_t *) yp_splat_node_create(parser, &operator, name); + return parse_targets(parser, splat, YP_BINDING_POWER_INDEX); + } + case YP_TOKEN_BANG: { + parser_lex(parser); + + yp_token_t operator = parser->previous; + yp_node_t *receiver = parse_expression(parser, yp_binding_powers[parser->previous.type].right, "Expected a receiver after unary !."); + yp_call_node_t *node = yp_call_node_unary_create(parser, &operator, receiver, "!"); + + return (yp_node_t *) node; + } + case YP_TOKEN_TILDE: { + parser_lex(parser); + + yp_token_t operator = parser->previous; + yp_node_t *receiver = parse_expression(parser, yp_binding_powers[parser->previous.type].right, "Expected a receiver after unary ~."); + yp_call_node_t *node = yp_call_node_unary_create(parser, &operator, receiver, "~"); + + return (yp_node_t *) node; + } + case YP_TOKEN_UMINUS: + case YP_TOKEN_UMINUS_NUM: { + parser_lex(parser); + + yp_token_t operator = parser->previous; + yp_node_t *receiver = parse_expression(parser, yp_binding_powers[parser->previous.type].right, "Expected a receiver after unary -."); + yp_call_node_t *node = yp_call_node_unary_create(parser, &operator, receiver, "-@"); + + return (yp_node_t *) node; + } + case YP_TOKEN_MINUS_GREATER: { + int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting; + parser->lambda_enclosure_nesting = parser->enclosure_nesting; + + yp_accepts_block_stack_push(parser, true); + parser_lex(parser); + + yp_token_t opening = parser->previous; + yp_parser_scope_push(parser, false); + yp_block_parameters_node_t *params; + + switch (parser->current.type) { + case YP_TOKEN_PARENTHESIS_LEFT: { + yp_token_t block_parameters_opening = parser->current; + parser_lex(parser); + + if (match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { + params = yp_block_parameters_node_create(parser, NULL, &block_parameters_opening); + } else { + params = parse_block_parameters(parser, false, &block_parameters_opening, true); + } + + accept(parser, YP_TOKEN_NEWLINE); + expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' after left parenthesis."); + yp_block_parameters_node_closing_set(params, &parser->previous); + + break; + } + case YP_CASE_PARAMETER: { + yp_token_t opening = not_provided(parser); + params = parse_block_parameters(parser, false, &opening, true); + break; + } + default: { + params = NULL; + break; + } + } + + yp_node_t *body = NULL; + parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting; + + if (accept(parser, YP_TOKEN_LAMBDA_BEGIN)) { + if (!accept(parser, YP_TOKEN_BRACE_RIGHT)) { + body = (yp_node_t *) parse_statements(parser, YP_CONTEXT_LAMBDA_BRACES); + expect(parser, YP_TOKEN_BRACE_RIGHT, "Expecting '}' to close lambda block."); + } + } else { + expect(parser, YP_TOKEN_KEYWORD_DO, "Expected a 'do' keyword or a '{' to open lambda block."); + + if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_END, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { + body = (yp_node_t *) parse_statements(parser, YP_CONTEXT_LAMBDA_DO_END); + } + + if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { + assert(body == NULL || body->type == YP_NODE_STATEMENTS_NODE); + body = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) body); + } + + expect(parser, YP_TOKEN_KEYWORD_END, "Expecting 'end' keyword to close lambda block."); + } + + yp_constant_id_list_t locals = parser->current_scope->locals; + yp_parser_scope_pop(parser); + yp_accepts_block_stack_pop(parser); + return (yp_node_t *) yp_lambda_node_create(parser, &locals, &opening, params, body, &parser->previous); + } + case YP_TOKEN_UPLUS: { + parser_lex(parser); + + yp_token_t operator = parser->previous; + yp_node_t *receiver = parse_expression(parser, yp_binding_powers[parser->previous.type].right, "Expected a receiver after unary +."); + yp_call_node_t *node = yp_call_node_unary_create(parser, &operator, receiver, "+@"); + + return (yp_node_t *) node; + } + case YP_TOKEN_STRING_BEGIN: { + assert(parser->lex_modes.current->mode == YP_LEX_STRING); + bool lex_interpolation = parser->lex_modes.current->as.string.interpolation; + + yp_token_t opening = parser->current; + parser_lex(parser); + + yp_node_t *node; + + if (accept(parser, YP_TOKEN_STRING_END)) { + // If we get here, then we have an end immediately after a start. In + // that case we'll create an empty content token and return an + // uninterpolated string. + yp_token_t content = (yp_token_t) { + .type = YP_TOKEN_STRING_CONTENT, + .start = parser->previous.start, + .end = parser->previous.start + }; + + node = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_NONE); + } else if (accept(parser, YP_TOKEN_LABEL_END)) { + // If we get here, then we have an end of a label immediately after a + // start. In that case we'll create an empty symbol node. + yp_token_t opening = not_provided(parser); + yp_token_t content = (yp_token_t) { + .type = YP_TOKEN_STRING_CONTENT, + .start = parser->previous.start, + .end = parser->previous.start + }; + + return (yp_node_t *) yp_symbol_node_create(parser, &opening, &content, &parser->previous); + } else if (!lex_interpolation) { + // If we don't accept interpolation then we expect the string to start + // with a single string content node. + expect(parser, YP_TOKEN_STRING_CONTENT, "Expected string content after opening delimiter."); + yp_token_t content = parser->previous; + + // It is unfortunately possible to have multiple string content nodes in + // a row in the case that there's heredoc content in the middle of the + // string, like this cursed example: + // + // <<-END+'b + // a + // END + // c'+'d' + // + // In that case we need to switch to an interpolated string to be able + // to contain all of the parts. + if (match_type_p(parser, YP_TOKEN_STRING_CONTENT)) { + yp_node_list_t parts = YP_EMPTY_NODE_LIST; + + yp_token_t delimiters = not_provided(parser); + yp_node_t *part = (yp_node_t *) yp_string_node_create_and_unescape(parser, &delimiters, &content, &delimiters, YP_UNESCAPE_MINIMAL); + yp_node_list_append(&parts, part); + + while (accept(parser, YP_TOKEN_STRING_CONTENT)) { + part = (yp_node_t *) yp_string_node_create_and_unescape(parser, &delimiters, &parser->previous, &delimiters, YP_UNESCAPE_MINIMAL); + yp_node_list_append(&parts, part); + } + + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a string literal."); + return (yp_node_t *) yp_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); + } + + if (accept(parser, YP_TOKEN_LABEL_END)) { + return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_ALL); + } + + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a string literal."); + node = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_MINIMAL); + } else if (match_type_p(parser, YP_TOKEN_STRING_CONTENT)) { + // In this case we've hit string content so we know the string at + // least has something in it. We'll need to check if the following + // token is the end (in which case we can return a plain string) or if + // it's not then it has interpolation. + yp_token_t content = parser->current; + parser_lex(parser); + + if (accept(parser, YP_TOKEN_STRING_END)) { + node = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_ALL); + } else if (accept(parser, YP_TOKEN_LABEL_END)) { + return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_ALL); + } else { + // If we get here, then we have interpolation so we'll need to create + // a string or symbol node with interpolation. + yp_node_list_t parts = YP_EMPTY_NODE_LIST; + yp_token_t string_opening = not_provided(parser); + yp_token_t string_closing = not_provided(parser); + yp_node_t *part = (yp_node_t *) yp_string_node_create_and_unescape(parser, &string_opening, &parser->previous, &string_closing, YP_UNESCAPE_ALL); + yp_node_list_append(&parts, part); + + while (!match_any_type_p(parser, 3, YP_TOKEN_STRING_END, YP_TOKEN_LABEL_END, YP_TOKEN_EOF)) { + yp_node_t *part = parse_string_part(parser); + if (part != NULL) yp_node_list_append(&parts, part); + } + + if (accept(parser, YP_TOKEN_LABEL_END)) { + return (yp_node_t *) yp_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous); + } + + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for an interpolated string."); + node = (yp_node_t *) yp_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); + } + } else { + // If we get here, then the first part of the string is not plain string + // content, in which case we need to parse the string as an interpolated + // string. + yp_node_list_t parts = YP_EMPTY_NODE_LIST; + + while (!match_any_type_p(parser, 3, YP_TOKEN_STRING_END, YP_TOKEN_LABEL_END, YP_TOKEN_EOF)) { + yp_node_t *part = parse_string_part(parser); + if (part != NULL) yp_node_list_append(&parts, part); + } + + if (accept(parser, YP_TOKEN_LABEL_END)) { + return (yp_node_t *) yp_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous); + } + + expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for an interpolated string."); + node = (yp_node_t *) yp_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); + } + + // If there's a string immediately following this string, then it's a + // concatenatation. In this case we'll parse the next string and create a + // node in the tree that concatenates the two strings. + if (parser->current.type == YP_TOKEN_STRING_BEGIN) { + return (yp_node_t *) yp_string_concat_node_create( + parser, + node, + parse_expression(parser, YP_BINDING_POWER_CALL, "Expected string on the right side of concatenation.") + ); + } else { + return node; + } + } + case YP_TOKEN_SYMBOL_BEGIN: { + yp_lex_mode_t lex_mode = *parser->lex_modes.current; + parser_lex(parser); + + return parse_symbol(parser, &lex_mode, YP_LEX_STATE_END); + } + default: + if (context_recoverable(parser, &parser->current)) { + parser->recovering = true; + } + + return (yp_node_t *) yp_missing_node_create(parser, parser->previous.start, parser->previous.end); + } +} + +static inline yp_node_t * +parse_assignment_value(yp_parser_t *parser, yp_binding_power_t previous_binding_power, yp_binding_power_t binding_power, const char *message) { + yp_node_t *value = parse_starred_expression(parser, binding_power, message); + + if (previous_binding_power == YP_BINDING_POWER_STATEMENT && accept(parser, YP_TOKEN_COMMA)) { + yp_token_t opening = not_provided(parser); + yp_array_node_t *array = yp_array_node_create(parser, &opening); + + yp_array_node_elements_append(array, value); + value = (yp_node_t *) array; + + do { + yp_node_t *element = parse_starred_expression(parser, binding_power, "Expected an element for the array."); + yp_array_node_elements_append(array, element); + if (element->type == YP_NODE_MISSING_NODE) break; + } while (accept(parser, YP_TOKEN_COMMA)); + } + + return value; +} + +static inline yp_node_t * +parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t previous_binding_power, yp_binding_power_t binding_power) { + yp_token_t token = parser->current; + + switch (token.type) { + case YP_TOKEN_EQUAL: { + switch (node->type) { + case YP_NODE_CALL_NODE: { + // If we have no arguments to the call node and we need this to be a + // target then this is either a method call or a local variable write. + // This _must_ happen before the value is parsed because it could be + // referenced in the value. + yp_call_node_t *call_node = (yp_call_node_t *) node; + if (yp_call_node_vcall_p(call_node)) { + yp_parser_local_add_location(parser, call_node->message_loc.start, call_node->message_loc.end); + } + } + /* fallthrough */ + case YP_CASE_WRITABLE: { + parser_lex(parser); + yp_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, "Expected a value after =."); + return parse_target(parser, node, &token, value); + } + case YP_NODE_SPLAT_NODE: { + yp_splat_node_t *splat_node = (yp_splat_node_t *) node; + + switch (splat_node->expression->type) { + case YP_CASE_WRITABLE: { + parser_lex(parser); + yp_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, "Expected a value after =."); + return parse_target(parser, (yp_node_t *) splat_node, &token, value); + } + default: {} + } + } + /* fallthrough */ + default: + parser_lex(parser); + + // In this case we have an = sign, but we don't know what it's for. We + // need to treat it as an error. For now, we'll mark it as an error + // and just skip right past it. + yp_diagnostic_list_append(&parser->error_list, token.start, token.end, "Unexpected `='."); + return node; + } + } + case YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL: { + switch (node->type) { + case YP_NODE_BACK_REFERENCE_READ_NODE: + case YP_NODE_NUMBERED_REFERENCE_READ_NODE: + yp_diagnostic_list_append(&parser->error_list, node->location.start, node->location.end, "Can't set variable"); + /* fallthrough */ + case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&="); + yp_node_t *result = (yp_node_t *) yp_global_variable_operator_and_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_CALL_NODE: { + yp_call_node_t *call_node = (yp_call_node_t *) node; + + // If we have a vcall (a method with no arguments and no + // receiver that could have been a local variable) then we + // will transform it into a local variable write. + if (yp_call_node_vcall_p(call_node)) { + yp_location_t message_loc = call_node->message_loc; + yp_parser_local_add_location(parser, message_loc.start, message_loc.end); + + if (token_is_numbered_parameter(message_loc.start, message_loc.end)) { + yp_diagnostic_list_append(&parser->error_list, message_loc.start, message_loc.end, "reserved for numbered parameter"); + } + + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&="); + yp_constant_id_t constant_id = yp_parser_constant_id_location(parser, message_loc.start, message_loc.end); + yp_node_t *result = (yp_node_t *) yp_local_variable_operator_and_write_node_create(parser, node, &token, value, constant_id); + + yp_node_destroy(parser, node); + return result; + } + + parser_lex(parser); + + yp_token_t operator = not_provided(parser); + node = parse_target(parser, node, &operator, NULL); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&="); + return (yp_node_t *) yp_call_operator_and_write_node_create(parser, call_node, &token, value); + } + case YP_NODE_CLASS_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&="); + yp_node_t *result = (yp_node_t *) yp_class_variable_operator_and_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_CONSTANT_PATH_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&="); + return (yp_node_t *) yp_constant_path_operator_and_write_node_create(parser, (yp_constant_path_node_t *) node, &token, value); + } + case YP_NODE_CONSTANT_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&="); + yp_node_t *result = (yp_node_t *) yp_constant_operator_and_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&="); + yp_node_t *result = (yp_node_t *) yp_instance_variable_operator_and_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_LOCAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&="); + yp_constant_id_t constant_id = ((yp_local_variable_read_node_t *) node)->constant_id; + yp_node_t *result = (yp_node_t *) yp_local_variable_operator_and_write_node_create(parser, node, &token, value, constant_id); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_MULTI_WRITE_NODE: { + parser_lex(parser); + yp_diagnostic_list_append(&parser->error_list, token.start, token.end, "Cannot use `&&=' on a multi-write."); + return node; + } + default: + parser_lex(parser); + + // In this case we have an &&= sign, but we don't know what it's for. + // We need to treat it as an error. For now, we'll mark it as an error + // and just skip right past it. + yp_diagnostic_list_append(&parser->error_list, token.start, token.end, "Unexpected `&&='."); + return node; + } + } + case YP_TOKEN_PIPE_PIPE_EQUAL: { + switch (node->type) { + case YP_NODE_BACK_REFERENCE_READ_NODE: + case YP_NODE_NUMBERED_REFERENCE_READ_NODE: + yp_diagnostic_list_append(&parser->error_list, node->location.start, node->location.end, "Can't set variable"); + /* fallthrough */ + case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||="); + yp_node_t *result = (yp_node_t *) yp_global_variable_operator_or_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_CALL_NODE: { + yp_call_node_t *call_node = (yp_call_node_t *) node; + + // If we have a vcall (a method with no arguments and no + // receiver that could have been a local variable) then we + // will transform it into a local variable write. + if (yp_call_node_vcall_p(call_node)) { + yp_location_t message_loc = call_node->message_loc; + yp_parser_local_add_location(parser, message_loc.start, message_loc.end); + + if (token_is_numbered_parameter(message_loc.start, message_loc.end)) { + yp_diagnostic_list_append(&parser->error_list, message_loc.start, message_loc.end, "reserved for numbered parameter"); + } + + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||="); + yp_constant_id_t constant_id = yp_parser_constant_id_location(parser, message_loc.start, message_loc.end); + yp_node_t *result = (yp_node_t *) yp_local_variable_operator_or_write_node_create(parser, node, &token, value, constant_id); + + yp_node_destroy(parser, node); + return result; + } + + parser_lex(parser); + + yp_token_t operator = not_provided(parser); + node = parse_target(parser, node, &operator, NULL); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||="); + return (yp_node_t *) yp_call_operator_or_write_node_create(parser, call_node, &token, value); + } + case YP_NODE_CLASS_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||="); + yp_node_t *result = (yp_node_t *) yp_class_variable_operator_or_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_CONSTANT_PATH_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||="); + return (yp_node_t *) yp_constant_path_operator_or_write_node_create(parser, (yp_constant_path_node_t *) node, &token, value); + } + case YP_NODE_CONSTANT_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||="); + yp_node_t *result = (yp_node_t *) yp_constant_operator_or_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||="); + yp_node_t *result = (yp_node_t *) yp_instance_variable_operator_or_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_LOCAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||="); + yp_constant_id_t constant_id = ((yp_local_variable_read_node_t *) node)->constant_id; + yp_node_t *result = (yp_node_t *) yp_local_variable_operator_or_write_node_create(parser, node, &token, value, constant_id); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_MULTI_WRITE_NODE: { + parser_lex(parser); + yp_diagnostic_list_append(&parser->error_list, token.start, token.end, "Cannot use `||=' on a multi-write."); + return node; + } + default: + parser_lex(parser); + + // In this case we have an ||= sign, but we don't know what it's for. + // We need to treat it as an error. For now, we'll mark it as an error + // and just skip right past it. + yp_diagnostic_list_append(&parser->error_list, token.start, token.end, "Unexpected `||='."); + return node; + } + } + case YP_TOKEN_AMPERSAND_EQUAL: + case YP_TOKEN_CARET_EQUAL: + case YP_TOKEN_GREATER_GREATER_EQUAL: + case YP_TOKEN_LESS_LESS_EQUAL: + case YP_TOKEN_MINUS_EQUAL: + case YP_TOKEN_PERCENT_EQUAL: + case YP_TOKEN_PIPE_EQUAL: + case YP_TOKEN_PLUS_EQUAL: + case YP_TOKEN_SLASH_EQUAL: + case YP_TOKEN_STAR_EQUAL: + case YP_TOKEN_STAR_STAR_EQUAL: { + switch (node->type) { + case YP_NODE_BACK_REFERENCE_READ_NODE: + case YP_NODE_NUMBERED_REFERENCE_READ_NODE: + yp_diagnostic_list_append(&parser->error_list, node->location.start, node->location.end, "Can't set variable"); + /* fallthrough */ + case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator"); + yp_node_t *result = (yp_node_t *) yp_global_variable_operator_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_CALL_NODE: { + yp_call_node_t *call_node = (yp_call_node_t *) node; + + // If we have a vcall (a method with no arguments and no + // receiver that could have been a local variable) then we + // will transform it into a local variable write. + if (yp_call_node_vcall_p(call_node)) { + yp_location_t message_loc = call_node->message_loc; + yp_parser_local_add_location(parser, message_loc.start, message_loc.end); + + if (token_is_numbered_parameter(message_loc.start, message_loc.end)) { + yp_diagnostic_list_append(&parser->error_list, message_loc.start, message_loc.end, "reserved for numbered parameter"); + } + + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&="); + yp_constant_id_t constant_id = yp_parser_constant_id_location(parser, message_loc.start, message_loc.end); + yp_node_t *result = (yp_node_t *) yp_local_variable_operator_write_node_create(parser, node, &token, value, constant_id); + + yp_node_destroy(parser, node); + return result; + } + + yp_token_t operator = not_provided(parser); + node = parse_target(parser, node, &operator, NULL); + + parser_lex(parser); + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator."); + return (yp_node_t *) yp_call_operator_write_node_create(parser, call_node, &token, value); + } + case YP_NODE_CLASS_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator."); + yp_node_t *result = (yp_node_t *) yp_class_variable_operator_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_CONSTANT_PATH_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator."); + return (yp_node_t *) yp_constant_path_operator_write_node_create(parser, (yp_constant_path_node_t *) node, &token, value); + } + case YP_NODE_CONSTANT_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator."); + yp_node_t *result = (yp_node_t *) yp_constant_operator_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator."); + yp_node_t *result = (yp_node_t *) yp_instance_variable_operator_write_node_create(parser, node, &token, value); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_LOCAL_VARIABLE_READ_NODE: { + parser_lex(parser); + + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator."); + yp_constant_id_t constant_id = ((yp_local_variable_read_node_t *) node)->constant_id; + yp_node_t *result = (yp_node_t *) yp_local_variable_operator_write_node_create(parser, node, &token, value, constant_id); + + yp_node_destroy(parser, node); + return result; + } + case YP_NODE_MULTI_WRITE_NODE: { + parser_lex(parser); + yp_diagnostic_list_append(&parser->error_list, token.start, token.end, "Unexpected operator."); + return node; + } + default: + parser_lex(parser); + + // In this case we have an operator but we don't know what it's for. + // We need to treat it as an error. For now, we'll mark it as an error + // and just skip right past it. + yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected operator."); + return node; + } + } + case YP_TOKEN_AMPERSAND_AMPERSAND: + case YP_TOKEN_KEYWORD_AND: { + parser_lex(parser); + + yp_node_t *right = parse_expression(parser, binding_power, "Expected a value after the operator."); + return (yp_node_t *) yp_and_node_create(parser, node, &token, right); + } + case YP_TOKEN_KEYWORD_OR: + case YP_TOKEN_PIPE_PIPE: { + parser_lex(parser); + + yp_node_t *right = parse_expression(parser, binding_power, "Expected a value after the operator."); + return (yp_node_t *) yp_or_node_create(parser, node, &token, right); + } + case YP_TOKEN_EQUAL_TILDE: { + // Note that we _must_ parse the value before adding the local variables + // in order to properly mirror the behavior of Ruby. For example, + // + // /(?bar)/ =~ foo + // + // In this case, `foo` should be a method call and not a local yet. + parser_lex(parser); + yp_node_t *argument = parse_expression(parser, binding_power, "Expected a value after the operator."); + + // If the receiver of this =~ is a regular expression node, then we need + // to introduce local variables for it based on its named capture groups. + if (node->type == YP_NODE_REGULAR_EXPRESSION_NODE) { + yp_string_list_t named_captures; + yp_string_list_init(&named_captures); + + yp_location_t *content_loc = &((yp_regular_expression_node_t *) node)->content_loc; + + YP_ATTRIBUTE_UNUSED bool captured_group_names = + yp_regexp_named_capture_group_names(content_loc->start, (size_t) (content_loc->end - content_loc->start), &named_captures); + + // We assert that the the regex was successfully parsed + assert(captured_group_names); + + for (size_t index = 0; index < named_captures.length; index++) { + yp_string_t *name = &named_captures.strings[index]; + assert(name->type == YP_STRING_SHARED); + + yp_parser_local_add_location(parser, name->as.shared.start, name->as.shared.end); + } + + yp_string_list_free(&named_captures); + } + + return (yp_node_t *) yp_call_node_binary_create(parser, node, &token, argument); + } + case YP_TOKEN_BANG_EQUAL: + case YP_TOKEN_BANG_TILDE: + case YP_TOKEN_EQUAL_EQUAL: + case YP_TOKEN_EQUAL_EQUAL_EQUAL: + case YP_TOKEN_LESS_EQUAL_GREATER: + case YP_TOKEN_GREATER: + case YP_TOKEN_GREATER_EQUAL: + case YP_TOKEN_LESS: + case YP_TOKEN_LESS_EQUAL: + case YP_TOKEN_CARET: + case YP_TOKEN_PIPE: + case YP_TOKEN_AMPERSAND: + case YP_TOKEN_GREATER_GREATER: + case YP_TOKEN_LESS_LESS: + case YP_TOKEN_MINUS: + case YP_TOKEN_PLUS: + case YP_TOKEN_PERCENT: + case YP_TOKEN_SLASH: + case YP_TOKEN_STAR: + case YP_TOKEN_STAR_STAR: { + parser_lex(parser); + + yp_node_t *argument = parse_expression(parser, binding_power, "Expected a value after the operator."); + return (yp_node_t *) yp_call_node_binary_create(parser, node, &token, argument); + } + case YP_TOKEN_AMPERSAND_DOT: + case YP_TOKEN_DOT: { + parser_lex(parser); + yp_token_t operator = parser->previous; + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + + // This if statement handles the foo.() syntax. + if (match_type_p(parser, YP_TOKEN_PARENTHESIS_LEFT)) { + parse_arguments_list(parser, &arguments, true); + return (yp_node_t *) yp_call_node_shorthand_create(parser, node, &operator, &arguments); + } + + yp_token_t message; + + switch (parser->current.type) { + case YP_CASE_OPERATOR: + case YP_CASE_KEYWORD: + case YP_TOKEN_CONSTANT: + case YP_TOKEN_IDENTIFIER: { + parser_lex(parser); + message = parser->previous; + break; + } + default: { + yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Expected a valid method name"); + message = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + } + } + + parse_arguments_list(parser, &arguments, true); + yp_call_node_t *call = yp_call_node_call_create(parser, node, &operator, &message, &arguments); + + if ( + (previous_binding_power == YP_BINDING_POWER_STATEMENT) && + arguments.arguments == NULL && + arguments.opening_loc.start == NULL && + match_type_p(parser, YP_TOKEN_COMMA) + ) { + return parse_targets(parser, (yp_node_t *) call, YP_BINDING_POWER_INDEX); + } else { + return (yp_node_t *) call; + } + } + case YP_TOKEN_DOT_DOT: + case YP_TOKEN_DOT_DOT_DOT: { + parser_lex(parser); + + yp_node_t *right = NULL; + if (token_begins_expression_p(parser->current.type)) { + right = parse_expression(parser, binding_power, "Expected a value after the operator."); + } + + return (yp_node_t *) yp_range_node_create(parser, node, &token, right); + } + case YP_TOKEN_KEYWORD_IF_MODIFIER: { + yp_token_t keyword = parser->current; + parser_lex(parser); + + yp_node_t *predicate = parse_expression(parser, binding_power, "Expected a predicate after `if'."); + return (yp_node_t *) yp_if_node_modifier_create(parser, node, &keyword, predicate); + } + case YP_TOKEN_KEYWORD_UNLESS_MODIFIER: { + yp_token_t keyword = parser->current; + parser_lex(parser); + + yp_node_t *predicate = parse_expression(parser, binding_power, "Expected a predicate after `unless'."); + return (yp_node_t *) yp_unless_node_modifier_create(parser, node, &keyword, predicate); + } + case YP_TOKEN_KEYWORD_UNTIL_MODIFIER: { + parser_lex(parser); + yp_statements_node_t *statements = yp_statements_node_create(parser); + yp_statements_node_body_append(statements, node); + + yp_node_t *predicate = parse_expression(parser, binding_power, "Expected a predicate after 'until'"); + return (yp_node_t *) yp_until_node_create(parser, &token, predicate, statements); + } + case YP_TOKEN_KEYWORD_WHILE_MODIFIER: { + parser_lex(parser); + yp_statements_node_t *statements = yp_statements_node_create(parser); + yp_statements_node_body_append(statements, node); + + yp_node_t *predicate = parse_expression(parser, binding_power, "Expected a predicate after 'while'"); + return (yp_node_t *) yp_while_node_create(parser, &token, predicate, statements); + } + case YP_TOKEN_QUESTION_MARK: { + parser_lex(parser); + yp_node_t *true_expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after '?'"); + + if (parser->recovering) { + // If parsing the true expression of this ternary resulted in a syntax + // error that we can recover from, then we're going to put missing nodes + // and tokens into the remaining places. We want to be sure to do this + // before the `expect` function call to make sure it doesn't + // accidentally move past a ':' token that occurs after the syntax + // error. + yp_token_t colon = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; + yp_node_t *false_expression = (yp_node_t *) yp_missing_node_create(parser, colon.start, colon.end); + + return (yp_node_t *) yp_if_node_ternary_create(parser, node, true_expression, &colon, false_expression); + } + + accept(parser, YP_TOKEN_NEWLINE); + expect(parser, YP_TOKEN_COLON, "Expected ':' after true expression in ternary operator."); + + yp_token_t colon = parser->previous; + yp_node_t *false_expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after ':'"); + + return (yp_node_t *) yp_if_node_ternary_create(parser, node, true_expression, &colon, false_expression); + } + case YP_TOKEN_COLON_COLON: { + parser_lex(parser); + yp_token_t delimiter = parser->previous; + + switch (parser->current.type) { + case YP_TOKEN_CONSTANT: { + parser_lex(parser); + yp_node_t *path; + + if ( + (parser->current.type == YP_TOKEN_PARENTHESIS_LEFT) || + (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR)) + ) { + // If we have a constant immediately following a '::' operator, then + // this can either be a constant path or a method call, depending on + // what follows the constant. + // + // If we have parentheses, then this is a method call. That would + // look like Foo::Bar(). + yp_token_t message = parser->previous; + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + + parse_arguments_list(parser, &arguments, true); + path = (yp_node_t *) yp_call_node_call_create(parser, node, &delimiter, &message, &arguments); + } else { + // Otherwise, this is a constant path. That would look like Foo::Bar. + yp_node_t *child = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); + path = (yp_node_t *)yp_constant_path_node_create(parser, node, &delimiter, child); + } + + // If this is followed by a comma then it is a multiple assignment. + if (previous_binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { + return parse_targets(parser, path, YP_BINDING_POWER_INDEX); + } + + return path; + } + case YP_CASE_OPERATOR: + case YP_CASE_KEYWORD: + case YP_TOKEN_IDENTIFIER: { + parser_lex(parser); + + // If we have an identifier following a '::' operator, then it is for + // sure a method call. + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + parse_arguments_list(parser, &arguments, true); + yp_call_node_t *call = yp_call_node_call_create(parser, node, &delimiter, &parser->previous, &arguments); + + // If this is followed by a comma then it is a multiple assignment. + if (previous_binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { + return parse_targets(parser, (yp_node_t *) call, YP_BINDING_POWER_INDEX); + } + + return (yp_node_t *) call; + } + case YP_TOKEN_PARENTHESIS_LEFT: { + // If we have a parenthesis following a '::' operator, then it is the + // method call shorthand. That would look like Foo::(bar). + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + parse_arguments_list(parser, &arguments, true); + + return (yp_node_t *) yp_call_node_shorthand_create(parser, node, &delimiter, &arguments); + } + default: { + yp_diagnostic_list_append(&parser->error_list, delimiter.start, delimiter.end, "Expected identifier or constant after '::'"); + yp_node_t *child = (yp_node_t *) yp_missing_node_create(parser, delimiter.start, delimiter.end); + return (yp_node_t *)yp_constant_path_node_create(parser, node, &delimiter, child); + } + } + } + case YP_TOKEN_KEYWORD_RESCUE_MODIFIER: { + parser_lex(parser); + accept(parser, YP_TOKEN_NEWLINE); + yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the rescue keyword."); + + return (yp_node_t *) yp_rescue_modifier_node_create(parser, node, &token, value); + } + case YP_TOKEN_BRACKET_LEFT: { + parser_lex(parser); + + yp_arguments_t arguments = YP_EMPTY_ARGUMENTS; + arguments.opening_loc = ((yp_location_t) { .start = parser->previous.start, .end = parser->previous.end }); + + if (!accept(parser, YP_TOKEN_BRACKET_RIGHT)) { + yp_accepts_block_stack_push(parser, true); + arguments.arguments = yp_arguments_node_create(parser); + + parse_arguments(parser, arguments.arguments, false, YP_TOKEN_BRACKET_RIGHT); + yp_accepts_block_stack_pop(parser); + + expect(parser, YP_TOKEN_BRACKET_RIGHT, "Expected ']' to close the bracket expression."); + } + + arguments.closing_loc = ((yp_location_t) { .start = parser->previous.start, .end = parser->previous.end }); + + // If we have a comma after the closing bracket then this is a multiple + // assignment and we should parse the targets. + if (previous_binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { + yp_call_node_t *aref = yp_call_node_aref_create(parser, node, &arguments); + return parse_targets(parser, (yp_node_t *) aref, YP_BINDING_POWER_INDEX); + } + + // If we're at the end of the arguments, we can now check if there is a + // block node that starts with a {. If there is, then we can parse it and + // add it to the arguments. + if (accept(parser, YP_TOKEN_BRACE_LEFT)) { + arguments.block = parse_block(parser); + } else if (yp_accepts_block_stack_p(parser) && accept(parser, YP_TOKEN_KEYWORD_DO)) { + arguments.block = parse_block(parser); + } + + return (yp_node_t *) yp_call_node_aref_create(parser, node, &arguments); + } + case YP_TOKEN_KEYWORD_IN: { + bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; + parser->pattern_matching_newlines = true; + + yp_token_t operator = parser->current; + parser->command_start = false; + lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); + + parser_lex(parser); + + yp_node_t *pattern = parse_pattern(parser, true, "Expected a pattern after `in'."); + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + + return (yp_node_t *) yp_match_predicate_node_create(parser, node, pattern, &operator); + } + case YP_TOKEN_EQUAL_GREATER: { + bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; + parser->pattern_matching_newlines = true; + + yp_token_t operator = parser->current; + parser->command_start = false; + lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); + + parser_lex(parser); + + yp_node_t *pattern = parse_pattern(parser, true, "Expected a pattern after `=>'."); + parser->pattern_matching_newlines = previous_pattern_matching_newlines; + + return (yp_node_t *) yp_match_required_node_create(parser, node, pattern, &operator); + } + default: + assert(false && "unreachable"); + return NULL; + } +} + +// Parse an expression at the given point of the parser using the given binding +// power to parse subsequent chains. If this function finds a syntax error, it +// will append the error message to the parser's error list. +// +// Consumers of this function should always check parser->recovering to +// determine if they need to perform additional cleanup. +static yp_node_t * +parse_expression(yp_parser_t *parser, yp_binding_power_t binding_power, const char *message) { + yp_token_t recovery = parser->previous; + yp_node_t *node = parse_expression_prefix(parser, binding_power); + + // If we found a syntax error, then the type of node returned by + // parse_expression_prefix is going to be a missing node. In that case we need + // to add the error message to the parser's error list. + if (node->type == YP_NODE_MISSING_NODE) { + yp_diagnostic_list_append(&parser->error_list, recovery.end, recovery.end, message); + return node; + } + + // Otherwise we'll look and see if the next token can be parsed as an infix + // operator. If it can, then we'll parse it using parse_expression_infix. + yp_binding_powers_t current_binding_powers; + while ( + current_binding_powers = yp_binding_powers[parser->current.type], + binding_power <= current_binding_powers.left && + current_binding_powers.binary + ) { + node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right); + } + + return node; +} + +static yp_node_t * +parse_program(yp_parser_t *parser) { + yp_parser_scope_push(parser, true); + parser_lex(parser); + + yp_statements_node_t *statements = parse_statements(parser, YP_CONTEXT_MAIN); + if (!statements) { + statements = yp_statements_node_create(parser); + } + yp_constant_id_list_t locals = parser->current_scope->locals; + yp_parser_scope_pop(parser); + + // If this is an empty file, then we're still going to parse all of the + // statements in order to gather up all of the comments and such. Here we'll + // correct the location information. + if (yp_statements_node_body_length(statements) == 0) { + yp_statements_node_location_set(statements, parser->start, parser->start); + } + + return (yp_node_t *) yp_program_node_create(parser, &locals, statements); +} + +/******************************************************************************/ +/* External functions */ +/******************************************************************************/ + +// Initialize a parser with the given start and end pointers. +YP_EXPORTED_FUNCTION void +yp_parser_init(yp_parser_t *parser, const char *source, size_t size, const char *filepath) { + // Set filepath to the file that was passed + if (!filepath) filepath = ""; + yp_string_t filepath_string; + yp_string_constant_init(&filepath_string, filepath, strlen(filepath)); + + *parser = (yp_parser_t) { + .lex_state = YP_LEX_STATE_BEG, + .command_start = true, + .enclosure_nesting = 0, + .lambda_enclosure_nesting = -1, + .brace_nesting = 0, + .lex_modes = { + .index = 0, + .stack = {{ .mode = YP_LEX_DEFAULT }}, + .current = &parser->lex_modes.stack[0], + }, + .start = source, + .end = source + size, + .previous = { .type = YP_TOKEN_EOF, .start = source, .end = source }, + .current = { .type = YP_TOKEN_EOF, .start = source, .end = source }, + .next_start = NULL, + .heredoc_end = NULL, + .current_scope = NULL, + .current_context = NULL, + .recovering = false, + .encoding = yp_encoding_utf_8, + .encoding_changed = false, + .encoding_changed_callback = NULL, + .encoding_decode_callback = NULL, + .encoding_comment_start = source, + .lex_callback = NULL, + .pattern_matching_newlines = false, + .in_keyword_arg = false, + .filepath_string = filepath_string, + }; + + yp_state_stack_init(&parser->do_loop_stack); + yp_state_stack_init(&parser->accepts_block_stack); + yp_accepts_block_stack_push(parser, true); + + yp_list_init(&parser->warning_list); + yp_list_init(&parser->error_list); + yp_list_init(&parser->comment_list); + + // Initialize the constant pool. We're going to completely guess as to the + // number of constants that we'll need based on the size of the input. The + // ratio we chose here is actually less arbitrary than you might think. + // + // We took ~50K Ruby files and measured the size of the file versus the + // number of constants that were found in those files. Then we found the + // average and standard deviation of the ratios of constants/bytesize. Then + // we added 1.34 standard deviations to the average to get a ratio that + // would fit 75% of the files (for a two-tailed distribution). This works + // because there was about a 0.77 correlation and the distribution was + // roughly normal. + // + // This ratio will need to change if we add more constants to the constant + // pool for another node type. + size_t constant_size = size / 95; + yp_constant_pool_init(&parser->constant_pool, constant_size < 4 ? 4 : constant_size); + + // Initialize the newline list. Similar to the constant pool, we're going to + // guess at the number of newlines that we'll need based on the size of the + // input. + size_t newline_size = size / 22; + yp_newline_list_init(&parser->newline_list, source, newline_size < 4 ? 4 : newline_size); + + assert(source != NULL); + if (size >= 3 && (unsigned char) source[0] == 0xef && (unsigned char) source[1] == 0xbb && (unsigned char) source[2] == 0xbf) { + // If the first three bytes of the source are the UTF-8 BOM, then we'll skip + // over them. + parser->current.end += 3; + } else if (size >= 2 && source[0] == '#' && source[1] == '!') { + // If the first two bytes of the source are a shebang, then we'll indicate + // that the encoding comment is at the end of the shebang. + const char *encoding_comment_start = next_newline(source, (ptrdiff_t) size); + if (encoding_comment_start) { + parser->encoding_comment_start = encoding_comment_start + 1; + } + } +} + +// Register a callback that will be called whenever YARP changes the encoding it +// is using to parse based on the magic comment. +YP_EXPORTED_FUNCTION void +yp_parser_register_encoding_changed_callback(yp_parser_t *parser, yp_encoding_changed_callback_t callback) { + parser->encoding_changed_callback = callback; +} + +// Register a callback that will be called when YARP encounters a magic comment +// with an encoding referenced that it doesn't understand. The callback should +// return NULL if it also doesn't understand the encoding or it should return a +// pointer to a yp_encoding_t struct that contains the functions necessary to +// parse identifiers. +YP_EXPORTED_FUNCTION void +yp_parser_register_encoding_decode_callback(yp_parser_t *parser, yp_encoding_decode_callback_t callback) { + parser->encoding_decode_callback = callback; +} + +// Free all of the memory associated with the comment list. +static inline void +yp_comment_list_free(yp_list_t *list) { + yp_list_node_t *node, *next; + + for (node = list->head; node != NULL; node = next) { + next = node->next; + + yp_comment_t *comment = (yp_comment_t *) node; + free(comment); + } +} + +// Free any memory associated with the given parser. +YP_EXPORTED_FUNCTION void +yp_parser_free(yp_parser_t *parser) { + yp_string_free(&parser->filepath_string); + yp_diagnostic_list_free(&parser->error_list); + yp_diagnostic_list_free(&parser->warning_list); + yp_comment_list_free(&parser->comment_list); + yp_constant_pool_free(&parser->constant_pool); + yp_newline_list_free(&parser->newline_list); + + while (parser->lex_modes.index >= YP_LEX_STACK_SIZE) { + lex_mode_pop(parser); + } +} + +// Parse the Ruby source associated with the given parser and return the tree. +YP_EXPORTED_FUNCTION yp_node_t * +yp_parse(yp_parser_t *parser) { + return parse_program(parser); +} + +YP_EXPORTED_FUNCTION void +yp_serialize(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer) { + yp_buffer_append_str(buffer, "YARP", 4); + yp_buffer_append_u8(buffer, YP_VERSION_MAJOR); + yp_buffer_append_u8(buffer, YP_VERSION_MINOR); + yp_buffer_append_u8(buffer, YP_VERSION_PATCH); + + yp_serialize_content(parser, node, buffer); + yp_buffer_append_str(buffer, "\0", 1); +} + +// Parse and serialize the AST represented by the given source to the given +// buffer. +YP_EXPORTED_FUNCTION void +yp_parse_serialize(const char *source, size_t size, yp_buffer_t *buffer) { + yp_parser_t parser; + yp_parser_init(&parser, source, size, NULL); + + yp_node_t *node = yp_parse(&parser); + yp_serialize(&parser, node, buffer); + + yp_node_destroy(&parser, node); + yp_parser_free(&parser); +} + +#undef YP_CASE_KEYWORD +#undef YP_CASE_OPERATOR +#undef YP_CASE_WRITABLE diff --git a/src/main/c/yarp/src/yarp/include/yarp.h b/src/main/c/yarp/src/yarp/include/yarp.h deleted file mode 100644 index 7b5c7ae54b6c..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp.h +++ /dev/null @@ -1,99 +0,0 @@ -#ifndef YARP_H -#define YARP_H - -#include "yarp/defines.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "yarp/missing.h" -#include "yarp/ast.h" -#include "yarp/diagnostic.h" -#include "yarp/node.h" -#include "yarp/pack.h" -#include "yarp/parser.h" -#include "yarp/regexp.h" -#include "yarp/unescape.h" -#include "yarp/util/yp_buffer.h" -#include "yarp/util/yp_char.h" -#include "yarp/util/yp_strpbrk.h" - -#define YP_VERSION_MAJOR 0 -#define YP_VERSION_MINOR 4 -#define YP_VERSION_PATCH 0 - -void -yp_serialize_node(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer); - -void -yp_print_node(yp_parser_t *parser, yp_node_t *node); - -// Returns the YARP version and notably the serialization format -__attribute__((__visibility__("default"))) extern const char * -yp_version(void); - -// Initialize a parser with the given start and end pointers. -__attribute__((__visibility__("default"))) extern void -yp_parser_init(yp_parser_t *parser, const char *source, size_t size, const char *filepath); - -// Register a callback that will be called whenever YARP changes the encoding it -// is using to parse based on the magic comment. -__attribute__((__visibility__("default"))) extern void -yp_parser_register_encoding_changed_callback(yp_parser_t *parser, yp_encoding_changed_callback_t callback); - -// Register a callback that will be called when YARP encounters a magic comment -// with an encoding referenced that it doesn't understand. The callback should -// return NULL if it also doesn't understand the encoding or it should return a -// pointer to a yp_encoding_t struct that contains the functions necessary to -// parse identifiers. -__attribute__((__visibility__("default"))) extern void -yp_parser_register_encoding_decode_callback(yp_parser_t *parser, yp_encoding_decode_callback_t callback); - -// Free any memory associated with the given parser. -__attribute__((__visibility__("default"))) extern void -yp_parser_free(yp_parser_t *parser); - -// Parse the Ruby source associated with the given parser and return the tree. -__attribute__((__visibility__("default"))) extern yp_node_t * -yp_parse(yp_parser_t *parser); - -// Deallocate a node and all of its children. -__attribute__((__visibility__("default"))) extern void -yp_node_destroy(yp_parser_t *parser, struct yp_node *node); - -// This struct stores the information gathered by the yp_node_memsize function. -// It contains both the memory footprint and additionally metadata about the -// shape of the tree. -typedef struct { - size_t memsize; - size_t node_count; -} yp_memsize_t; - -// Calculates the memory footprint of a given node. -__attribute__((__visibility__("default"))) extern void -yp_node_memsize(yp_node_t *node, yp_memsize_t *memsize); - -// Pretty-prints the AST represented by the given node to the given buffer. -__attribute__((__visibility__("default"))) extern void -yp_prettyprint(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer); - -// Serialize the AST represented by the given node to the given buffer. -__attribute__((__visibility__("default"))) extern void -yp_serialize(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer); - -// Parse and serialize the AST represented by the given source to the given -// buffer. -__attribute__((__visibility__("default"))) extern void -yp_parse_serialize(const char *source, size_t size, yp_buffer_t *buffer); - -// Returns a string representation of the given token type. -__attribute__((__visibility__("default"))) extern const char * -yp_token_type_to_str(yp_token_type_t token_type); - -#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/ast.h b/src/main/c/yarp/src/yarp/include/yarp/ast.h deleted file mode 100644 index c97c82d58e7c..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp/ast.h +++ /dev/null @@ -1,1155 +0,0 @@ -/******************************************************************************/ -/* This file is generated by the bin/template script and should not be */ -/* modified manually. See */ -/* bin/templates/include/yarp/ast.h.erb */ -/* if you are looking to modify the */ -/* template */ -/******************************************************************************/ -#ifndef YARP_AST_H -#define YARP_AST_H - -#include "yarp/defines.h" - -#include -#include -#include - -#include "yarp/util/yp_string.h" - -// This enum represents every type of token in the Ruby source. -typedef enum yp_token_type { - YP_TOKEN_EOF = 1, // final token in the file - YP_TOKEN_MISSING, // a token that was expected but not found - YP_TOKEN_NOT_PROVIDED, // a token that was not present but it is okay - YP_TOKEN_AMPERSAND, // & - YP_TOKEN_AMPERSAND_AMPERSAND, // && - YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL, // &&= - YP_TOKEN_AMPERSAND_DOT, // &. - YP_TOKEN_AMPERSAND_EQUAL, // &= - YP_TOKEN_BACKTICK, // ` - YP_TOKEN_BACK_REFERENCE, // a back reference - YP_TOKEN_BANG, // ! or !@ - YP_TOKEN_BANG_EQUAL, // != - YP_TOKEN_BANG_TILDE, // !~ - YP_TOKEN_BRACE_LEFT, // { - YP_TOKEN_BRACE_RIGHT, // } - YP_TOKEN_BRACKET_LEFT, // [ - YP_TOKEN_BRACKET_LEFT_ARRAY, // [ for the beginning of an array - YP_TOKEN_BRACKET_LEFT_RIGHT, // [] - YP_TOKEN_BRACKET_LEFT_RIGHT_EQUAL, // []= - YP_TOKEN_BRACKET_RIGHT, // ] - YP_TOKEN_CARET, // ^ - YP_TOKEN_CARET_EQUAL, // ^= - YP_TOKEN_CHARACTER_LITERAL, // a character literal - YP_TOKEN_CLASS_VARIABLE, // a class variable - YP_TOKEN_COLON, // : - YP_TOKEN_COLON_COLON, // :: - YP_TOKEN_COMMA, // , - YP_TOKEN_COMMENT, // a comment - YP_TOKEN_CONSTANT, // a constant - YP_TOKEN_DOT, // . - YP_TOKEN_DOT_DOT, // .. - YP_TOKEN_DOT_DOT_DOT, // ... - YP_TOKEN_EMBDOC_BEGIN, // =begin - YP_TOKEN_EMBDOC_END, // =end - YP_TOKEN_EMBDOC_LINE, // a line inside of embedded documentation - YP_TOKEN_EMBEXPR_BEGIN, // #{ - YP_TOKEN_EMBEXPR_END, // } - YP_TOKEN_EMBVAR, // # - YP_TOKEN_EQUAL, // = - YP_TOKEN_EQUAL_EQUAL, // == - YP_TOKEN_EQUAL_EQUAL_EQUAL, // === - YP_TOKEN_EQUAL_GREATER, // => - YP_TOKEN_EQUAL_TILDE, // =~ - YP_TOKEN_FLOAT, // a floating point number - YP_TOKEN_GLOBAL_VARIABLE, // a global variable - YP_TOKEN_GREATER, // > - YP_TOKEN_GREATER_EQUAL, // >= - YP_TOKEN_GREATER_GREATER, // >> - YP_TOKEN_GREATER_GREATER_EQUAL, // >>= - YP_TOKEN_HEREDOC_END, // the end of a heredoc - YP_TOKEN_HEREDOC_START, // the start of a heredoc - YP_TOKEN_IDENTIFIER, // an identifier - YP_TOKEN_IGNORED_NEWLINE, // an ignored newline - YP_TOKEN_IMAGINARY_NUMBER, // an imaginary number literal - YP_TOKEN_INSTANCE_VARIABLE, // an instance variable - YP_TOKEN_INTEGER, // an integer (any base) - YP_TOKEN_KEYWORD_ALIAS, // alias - YP_TOKEN_KEYWORD_AND, // and - YP_TOKEN_KEYWORD_BEGIN, // begin - YP_TOKEN_KEYWORD_BEGIN_UPCASE, // BEGIN - YP_TOKEN_KEYWORD_BREAK, // break - YP_TOKEN_KEYWORD_CASE, // case - YP_TOKEN_KEYWORD_CLASS, // class - YP_TOKEN_KEYWORD_DEF, // def - YP_TOKEN_KEYWORD_DEFINED, // defined? - YP_TOKEN_KEYWORD_DO, // do - YP_TOKEN_KEYWORD_DO_LOOP, // do keyword for a predicate in a while, until, or for loop - YP_TOKEN_KEYWORD_ELSE, // else - YP_TOKEN_KEYWORD_ELSIF, // elsif - YP_TOKEN_KEYWORD_END, // end - YP_TOKEN_KEYWORD_END_UPCASE, // END - YP_TOKEN_KEYWORD_ENSURE, // ensure - YP_TOKEN_KEYWORD_FALSE, // false - YP_TOKEN_KEYWORD_FOR, // for - YP_TOKEN_KEYWORD_IF, // if - YP_TOKEN_KEYWORD_IF_MODIFIER, // if in the modifier form - YP_TOKEN_KEYWORD_IN, // in - YP_TOKEN_KEYWORD_MODULE, // module - YP_TOKEN_KEYWORD_NEXT, // next - YP_TOKEN_KEYWORD_NIL, // nil - YP_TOKEN_KEYWORD_NOT, // not - YP_TOKEN_KEYWORD_OR, // or - YP_TOKEN_KEYWORD_REDO, // redo - YP_TOKEN_KEYWORD_RESCUE, // rescue - YP_TOKEN_KEYWORD_RESCUE_MODIFIER, // rescue in the modifier form - YP_TOKEN_KEYWORD_RETRY, // retry - YP_TOKEN_KEYWORD_RETURN, // return - YP_TOKEN_KEYWORD_SELF, // self - YP_TOKEN_KEYWORD_SUPER, // super - YP_TOKEN_KEYWORD_THEN, // then - YP_TOKEN_KEYWORD_TRUE, // true - YP_TOKEN_KEYWORD_UNDEF, // undef - YP_TOKEN_KEYWORD_UNLESS, // unless - YP_TOKEN_KEYWORD_UNLESS_MODIFIER, // unless in the modifier form - YP_TOKEN_KEYWORD_UNTIL, // until - YP_TOKEN_KEYWORD_UNTIL_MODIFIER, // until in the modifier form - YP_TOKEN_KEYWORD_WHEN, // when - YP_TOKEN_KEYWORD_WHILE, // while - YP_TOKEN_KEYWORD_WHILE_MODIFIER, // while in the modifier form - YP_TOKEN_KEYWORD_YIELD, // yield - YP_TOKEN_KEYWORD___ENCODING__, // __ENCODING__ - YP_TOKEN_KEYWORD___FILE__, // __FILE__ - YP_TOKEN_KEYWORD___LINE__, // __LINE__ - YP_TOKEN_LABEL, // a label - YP_TOKEN_LABEL_END, // the end of a label - YP_TOKEN_LAMBDA_BEGIN, // { - YP_TOKEN_LESS, // < - YP_TOKEN_LESS_EQUAL, // <= - YP_TOKEN_LESS_EQUAL_GREATER, // <=> - YP_TOKEN_LESS_LESS, // << - YP_TOKEN_LESS_LESS_EQUAL, // <<= - YP_TOKEN_MINUS, // - - YP_TOKEN_MINUS_EQUAL, // -= - YP_TOKEN_MINUS_GREATER, // -> - YP_TOKEN_NEWLINE, // a newline character outside of other tokens - YP_TOKEN_NTH_REFERENCE, // an nth global variable reference - YP_TOKEN_PARENTHESIS_LEFT, // ( - YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES, // ( for a parentheses node - YP_TOKEN_PARENTHESIS_RIGHT, // ) - YP_TOKEN_PERCENT, // % - YP_TOKEN_PERCENT_EQUAL, // %= - YP_TOKEN_PERCENT_LOWER_I, // %i - YP_TOKEN_PERCENT_LOWER_W, // %w - YP_TOKEN_PERCENT_LOWER_X, // %x - YP_TOKEN_PERCENT_UPPER_I, // %I - YP_TOKEN_PERCENT_UPPER_W, // %W - YP_TOKEN_PIPE, // | - YP_TOKEN_PIPE_EQUAL, // |= - YP_TOKEN_PIPE_PIPE, // || - YP_TOKEN_PIPE_PIPE_EQUAL, // ||= - YP_TOKEN_PLUS, // + - YP_TOKEN_PLUS_EQUAL, // += - YP_TOKEN_QUESTION_MARK, // ? - YP_TOKEN_RATIONAL_NUMBER, // a rational number literal - YP_TOKEN_REGEXP_BEGIN, // the beginning of a regular expression - YP_TOKEN_REGEXP_END, // the end of a regular expression - YP_TOKEN_SEMICOLON, // ; - YP_TOKEN_SLASH, // / - YP_TOKEN_SLASH_EQUAL, // /= - YP_TOKEN_STAR, // * - YP_TOKEN_STAR_EQUAL, // *= - YP_TOKEN_STAR_STAR, // ** - YP_TOKEN_STAR_STAR_EQUAL, // **= - YP_TOKEN_STRING_BEGIN, // the beginning of a string - YP_TOKEN_STRING_CONTENT, // the contents of a string - YP_TOKEN_STRING_END, // the end of a string - YP_TOKEN_SYMBOL_BEGIN, // the beginning of a symbol - YP_TOKEN_TILDE, // ~ or ~@ - YP_TOKEN_UCOLON_COLON, // unary :: - YP_TOKEN_UDOT_DOT, // unary .. - YP_TOKEN_UDOT_DOT_DOT, // unary ... - YP_TOKEN_UMINUS, // -@ - YP_TOKEN_UMINUS_NUM, // -@ for a number - YP_TOKEN_UPLUS, // +@ - YP_TOKEN_USTAR, // unary * - YP_TOKEN_USTAR_STAR, // unary ** - YP_TOKEN_WORDS_SEP, // a separator between words in a list - YP_TOKEN___END__, // marker for the point in the file at which the parser should stop - YP_TOKEN_MAXIMUM, // the maximum token value -} yp_token_type_t; - -// This struct represents a token in the Ruby source. We use it to track both -// type and location information. -typedef struct { - yp_token_type_t type; - const char *start; - const char *end; -} yp_token_t; - -typedef struct { - yp_token_t *tokens; - size_t size; - size_t capacity; -} yp_token_list_t; - -struct yp_node; - -typedef struct yp_node_list { - struct yp_node **nodes; - size_t size; - size_t capacity; -} yp_node_list_t; - -typedef enum { - YP_NODE_ALIAS_NODE = 1, - YP_NODE_ALTERNATION_PATTERN_NODE = 2, - YP_NODE_AND_NODE = 3, - YP_NODE_ARGUMENTS_NODE = 4, - YP_NODE_ARRAY_NODE = 5, - YP_NODE_ARRAY_PATTERN_NODE = 6, - YP_NODE_ASSOC_NODE = 7, - YP_NODE_ASSOC_SPLAT_NODE = 8, - YP_NODE_BEGIN_NODE = 9, - YP_NODE_BLOCK_ARGUMENT_NODE = 10, - YP_NODE_BLOCK_NODE = 11, - YP_NODE_BLOCK_PARAMETER_NODE = 12, - YP_NODE_BLOCK_PARAMETERS_NODE = 13, - YP_NODE_BREAK_NODE = 14, - YP_NODE_CALL_NODE = 15, - YP_NODE_CAPTURE_PATTERN_NODE = 16, - YP_NODE_CASE_NODE = 17, - YP_NODE_CLASS_NODE = 18, - YP_NODE_CLASS_VARIABLE_READ_NODE = 19, - YP_NODE_CLASS_VARIABLE_WRITE_NODE = 20, - YP_NODE_CONSTANT_PATH_NODE = 21, - YP_NODE_CONSTANT_PATH_WRITE_NODE = 22, - YP_NODE_CONSTANT_READ_NODE = 23, - YP_NODE_DEF_NODE = 24, - YP_NODE_DEFINED_NODE = 25, - YP_NODE_ELSE_NODE = 26, - YP_NODE_ENSURE_NODE = 27, - YP_NODE_FALSE_NODE = 28, - YP_NODE_FIND_PATTERN_NODE = 29, - YP_NODE_FLOAT_NODE = 30, - YP_NODE_FOR_NODE = 31, - YP_NODE_FORWARDING_ARGUMENTS_NODE = 32, - YP_NODE_FORWARDING_PARAMETER_NODE = 33, - YP_NODE_FORWARDING_SUPER_NODE = 34, - YP_NODE_GLOBAL_VARIABLE_READ_NODE = 35, - YP_NODE_GLOBAL_VARIABLE_WRITE_NODE = 36, - YP_NODE_HASH_NODE = 37, - YP_NODE_HASH_PATTERN_NODE = 38, - YP_NODE_IF_NODE = 39, - YP_NODE_IMAGINARY_NODE = 40, - YP_NODE_IN_NODE = 41, - YP_NODE_INSTANCE_VARIABLE_READ_NODE = 42, - YP_NODE_INSTANCE_VARIABLE_WRITE_NODE = 43, - YP_NODE_INTEGER_NODE = 44, - YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE = 45, - YP_NODE_INTERPOLATED_STRING_NODE = 46, - YP_NODE_INTERPOLATED_SYMBOL_NODE = 47, - YP_NODE_INTERPOLATED_X_STRING_NODE = 48, - YP_NODE_KEYWORD_HASH_NODE = 49, - YP_NODE_KEYWORD_PARAMETER_NODE = 50, - YP_NODE_KEYWORD_REST_PARAMETER_NODE = 51, - YP_NODE_LAMBDA_NODE = 52, - YP_NODE_LOCAL_VARIABLE_READ_NODE = 53, - YP_NODE_LOCAL_VARIABLE_WRITE_NODE = 54, - YP_NODE_MATCH_PREDICATE_NODE = 55, - YP_NODE_MATCH_REQUIRED_NODE = 56, - YP_NODE_MISSING_NODE = 57, - YP_NODE_MODULE_NODE = 58, - YP_NODE_MULTI_WRITE_NODE = 59, - YP_NODE_NEXT_NODE = 60, - YP_NODE_NIL_NODE = 61, - YP_NODE_NO_KEYWORDS_PARAMETER_NODE = 62, - YP_NODE_OPERATOR_AND_ASSIGNMENT_NODE = 63, - YP_NODE_OPERATOR_ASSIGNMENT_NODE = 64, - YP_NODE_OPERATOR_OR_ASSIGNMENT_NODE = 65, - YP_NODE_OPTIONAL_PARAMETER_NODE = 66, - YP_NODE_OR_NODE = 67, - YP_NODE_PARAMETERS_NODE = 68, - YP_NODE_PARENTHESES_NODE = 69, - YP_NODE_PINNED_EXPRESSION_NODE = 70, - YP_NODE_PINNED_VARIABLE_NODE = 71, - YP_NODE_POST_EXECUTION_NODE = 72, - YP_NODE_PRE_EXECUTION_NODE = 73, - YP_NODE_PROGRAM_NODE = 74, - YP_NODE_RANGE_NODE = 75, - YP_NODE_RATIONAL_NODE = 76, - YP_NODE_REDO_NODE = 77, - YP_NODE_REGULAR_EXPRESSION_NODE = 78, - YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE = 79, - YP_NODE_REQUIRED_PARAMETER_NODE = 80, - YP_NODE_RESCUE_MODIFIER_NODE = 81, - YP_NODE_RESCUE_NODE = 82, - YP_NODE_REST_PARAMETER_NODE = 83, - YP_NODE_RETRY_NODE = 84, - YP_NODE_RETURN_NODE = 85, - YP_NODE_SELF_NODE = 86, - YP_NODE_SINGLETON_CLASS_NODE = 87, - YP_NODE_SOURCE_ENCODING_NODE = 88, - YP_NODE_SOURCE_FILE_NODE = 89, - YP_NODE_SOURCE_LINE_NODE = 90, - YP_NODE_SPLAT_NODE = 91, - YP_NODE_STATEMENTS_NODE = 92, - YP_NODE_STRING_CONCAT_NODE = 93, - YP_NODE_STRING_INTERPOLATED_NODE = 94, - YP_NODE_STRING_NODE = 95, - YP_NODE_SUPER_NODE = 96, - YP_NODE_SYMBOL_NODE = 97, - YP_NODE_TRUE_NODE = 98, - YP_NODE_UNDEF_NODE = 99, - YP_NODE_UNLESS_NODE = 100, - YP_NODE_UNTIL_NODE = 101, - YP_NODE_WHEN_NODE = 102, - YP_NODE_WHILE_NODE = 103, - YP_NODE_X_STRING_NODE = 104, - YP_NODE_YIELD_NODE = 105, -} yp_node_type_t; - -// This represents a range of bytes in the source string to which a node or -// token corresponds. -typedef struct { - const char *start; - const char *end; -} yp_location_t; - -// This is the overall tagged union representing a node in the syntax tree. -typedef struct yp_node { - // This represents the type of the node. It somewhat maps to the nodes that - // existed in the original grammar and ripper, but it's not a 1:1 mapping. - yp_node_type_t type; - - // This is the location of the node in the source. It's a range of bytes - // containing a start and an end. - yp_location_t location; -} yp_node_t; - -// AliasNode -typedef struct yp_alias_node { - yp_node_t base; - struct yp_node *new_name; - struct yp_node *old_name; - yp_location_t keyword_loc; -} yp_alias_node_t; - -// AlternationPatternNode -typedef struct yp_alternation_pattern_node { - yp_node_t base; - struct yp_node *left; - struct yp_node *right; - yp_location_t operator_loc; -} yp_alternation_pattern_node_t; - -// AndNode -typedef struct yp_and_node { - yp_node_t base; - struct yp_node *left; - struct yp_node *right; - yp_token_t operator; -} yp_and_node_t; - -// ArgumentsNode -typedef struct yp_arguments_node { - yp_node_t base; - struct yp_node_list arguments; -} yp_arguments_node_t; - -// ArrayNode -typedef struct yp_array_node { - yp_node_t base; - struct yp_node_list elements; - yp_token_t opening; - yp_token_t closing; -} yp_array_node_t; - -// ArrayPatternNode -typedef struct yp_array_pattern_node { - yp_node_t base; - struct yp_node *constant; - struct yp_node_list requireds; - struct yp_node *rest; - struct yp_node_list posts; - yp_location_t opening_loc; - yp_location_t closing_loc; -} yp_array_pattern_node_t; - -// AssocNode -typedef struct yp_assoc_node { - yp_node_t base; - struct yp_node *key; - struct yp_node *value; - yp_token_t operator; -} yp_assoc_node_t; - -// AssocSplatNode -typedef struct yp_assoc_splat_node { - yp_node_t base; - struct yp_node *value; - yp_location_t operator_loc; -} yp_assoc_splat_node_t; - -// BeginNode -typedef struct yp_begin_node { - yp_node_t base; - yp_token_t begin_keyword; - struct yp_statements_node *statements; - struct yp_rescue_node *rescue_clause; - struct yp_else_node *else_clause; - struct yp_ensure_node *ensure_clause; - yp_token_t end_keyword; -} yp_begin_node_t; - -// BlockArgumentNode -typedef struct yp_block_argument_node { - yp_node_t base; - struct yp_node *expression; - yp_location_t operator_loc; -} yp_block_argument_node_t; - -// BlockNode -typedef struct yp_block_node { - yp_node_t base; - yp_token_list_t locals; - struct yp_block_parameters_node *parameters; - struct yp_node *statements; - yp_location_t opening_loc; - yp_location_t closing_loc; -} yp_block_node_t; - -// BlockParameterNode -typedef struct yp_block_parameter_node { - yp_node_t base; - yp_token_t name; - yp_location_t operator_loc; -} yp_block_parameter_node_t; - -// BlockParametersNode -typedef struct yp_block_parameters_node { - yp_node_t base; - struct yp_parameters_node *parameters; - yp_token_list_t locals; - yp_location_t opening_loc; - yp_location_t closing_loc; -} yp_block_parameters_node_t; - -// BreakNode -typedef struct yp_break_node { - yp_node_t base; - struct yp_arguments_node *arguments; - yp_location_t keyword_loc; -} yp_break_node_t; - -// CallNode -typedef struct yp_call_node { - yp_node_t base; - struct yp_node *receiver; - yp_token_t call_operator; - yp_token_t message; - yp_token_t opening; - struct yp_arguments_node *arguments; - yp_token_t closing; - struct yp_block_node *block; - yp_string_t name; -} yp_call_node_t; - -// CapturePatternNode -typedef struct yp_capture_pattern_node { - yp_node_t base; - struct yp_node *value; - struct yp_node *target; - yp_location_t operator_loc; -} yp_capture_pattern_node_t; - -// CaseNode -typedef struct yp_case_node { - yp_node_t base; - struct yp_node *predicate; - struct yp_node_list conditions; - struct yp_else_node *consequent; - yp_location_t case_keyword_loc; - yp_location_t end_keyword_loc; -} yp_case_node_t; - -// ClassNode -typedef struct yp_class_node { - yp_node_t base; - yp_token_list_t locals; - yp_token_t class_keyword; - struct yp_node *constant_path; - yp_token_t inheritance_operator; - struct yp_node *superclass; - struct yp_node *statements; - yp_token_t end_keyword; -} yp_class_node_t; - -// ClassVariableReadNode -typedef struct yp_class_variable_read_node { - yp_node_t base; -} yp_class_variable_read_node_t; - -// ClassVariableWriteNode -typedef struct yp_class_variable_write_node { - yp_node_t base; - yp_location_t name_loc; - struct yp_node *value; - yp_location_t operator_loc; -} yp_class_variable_write_node_t; - -// ConstantPathNode -typedef struct yp_constant_path_node { - yp_node_t base; - struct yp_node *parent; - struct yp_node *child; - yp_location_t delimiter_loc; -} yp_constant_path_node_t; - -// ConstantPathWriteNode -typedef struct yp_constant_path_write_node { - yp_node_t base; - struct yp_node *target; - yp_token_t operator; - struct yp_node *value; -} yp_constant_path_write_node_t; - -// ConstantReadNode -typedef struct yp_constant_read_node { - yp_node_t base; -} yp_constant_read_node_t; - -// DefNode -typedef struct yp_def_node { - yp_node_t base; - yp_token_t name; - struct yp_node *receiver; - struct yp_parameters_node *parameters; - struct yp_node *statements; - yp_token_list_t locals; - yp_location_t def_keyword_loc; - yp_location_t operator_loc; - yp_location_t lparen_loc; - yp_location_t rparen_loc; - yp_location_t equal_loc; - yp_location_t end_keyword_loc; -} yp_def_node_t; - -// DefinedNode -typedef struct yp_defined_node { - yp_node_t base; - yp_token_t lparen; - struct yp_node *value; - yp_token_t rparen; - yp_location_t keyword_loc; -} yp_defined_node_t; - -// ElseNode -typedef struct yp_else_node { - yp_node_t base; - yp_token_t else_keyword; - struct yp_statements_node *statements; - yp_token_t end_keyword; -} yp_else_node_t; - -// EnsureNode -typedef struct yp_ensure_node { - yp_node_t base; - yp_token_t ensure_keyword; - struct yp_statements_node *statements; - yp_token_t end_keyword; -} yp_ensure_node_t; - -// FalseNode -typedef struct yp_false_node { - yp_node_t base; -} yp_false_node_t; - -// FindPatternNode -typedef struct yp_find_pattern_node { - yp_node_t base; - struct yp_node *constant; - struct yp_node *left; - struct yp_node_list requireds; - struct yp_node *right; - yp_location_t opening_loc; - yp_location_t closing_loc; -} yp_find_pattern_node_t; - -// FloatNode -typedef struct yp_float_node { - yp_node_t base; -} yp_float_node_t; - -// ForNode -typedef struct yp_for_node { - yp_node_t base; - struct yp_node *index; - struct yp_node *collection; - struct yp_statements_node *statements; - yp_location_t for_keyword_loc; - yp_location_t in_keyword_loc; - yp_location_t do_keyword_loc; - yp_location_t end_keyword_loc; -} yp_for_node_t; - -// ForwardingArgumentsNode -typedef struct yp_forwarding_arguments_node { - yp_node_t base; -} yp_forwarding_arguments_node_t; - -// ForwardingParameterNode -typedef struct yp_forwarding_parameter_node { - yp_node_t base; -} yp_forwarding_parameter_node_t; - -// ForwardingSuperNode -typedef struct yp_forwarding_super_node { - yp_node_t base; - struct yp_block_node *block; -} yp_forwarding_super_node_t; - -// GlobalVariableReadNode -typedef struct yp_global_variable_read_node { - yp_node_t base; - yp_token_t name; -} yp_global_variable_read_node_t; - -// GlobalVariableWriteNode -typedef struct yp_global_variable_write_node { - yp_node_t base; - yp_token_t name; - yp_token_t operator; - struct yp_node *value; -} yp_global_variable_write_node_t; - -// HashNode -typedef struct yp_hash_node { - yp_node_t base; - yp_token_t opening; - struct yp_node_list elements; - yp_token_t closing; -} yp_hash_node_t; - -// HashPatternNode -typedef struct yp_hash_pattern_node { - yp_node_t base; - struct yp_node *constant; - struct yp_node_list assocs; - struct yp_node *kwrest; - yp_location_t opening_loc; - yp_location_t closing_loc; -} yp_hash_pattern_node_t; - -// IfNode -typedef struct yp_if_node { - yp_node_t base; - yp_token_t if_keyword; - struct yp_node *predicate; - struct yp_statements_node *statements; - struct yp_node *consequent; - yp_token_t end_keyword; -} yp_if_node_t; - -// ImaginaryNode -typedef struct yp_imaginary_node { - yp_node_t base; - struct yp_node *numeric; -} yp_imaginary_node_t; - -// InNode -typedef struct yp_in_node { - yp_node_t base; - struct yp_node *pattern; - struct yp_statements_node *statements; - yp_location_t in_loc; - yp_location_t then_loc; -} yp_in_node_t; - -// InstanceVariableReadNode -typedef struct yp_instance_variable_read_node { - yp_node_t base; -} yp_instance_variable_read_node_t; - -// InstanceVariableWriteNode -typedef struct yp_instance_variable_write_node { - yp_node_t base; - yp_location_t name_loc; - struct yp_node *value; - yp_location_t operator_loc; -} yp_instance_variable_write_node_t; - -// IntegerNode -typedef struct yp_integer_node { - yp_node_t base; -} yp_integer_node_t; - -// InterpolatedRegularExpressionNode -typedef struct yp_interpolated_regular_expression_node { - yp_node_t base; - yp_token_t opening; - struct yp_node_list parts; - yp_token_t closing; -} yp_interpolated_regular_expression_node_t; - -// InterpolatedStringNode -typedef struct yp_interpolated_string_node { - yp_node_t base; - yp_token_t opening; - struct yp_node_list parts; - yp_token_t closing; -} yp_interpolated_string_node_t; - -// InterpolatedSymbolNode -typedef struct yp_interpolated_symbol_node { - yp_node_t base; - yp_token_t opening; - struct yp_node_list parts; - yp_token_t closing; -} yp_interpolated_symbol_node_t; - -// InterpolatedXStringNode -typedef struct yp_interpolated_x_string_node { - yp_node_t base; - yp_token_t opening; - struct yp_node_list parts; - yp_token_t closing; -} yp_interpolated_x_string_node_t; - -// KeywordHashNode -typedef struct yp_keyword_hash_node { - yp_node_t base; - struct yp_node_list elements; -} yp_keyword_hash_node_t; - -// KeywordParameterNode -typedef struct yp_keyword_parameter_node { - yp_node_t base; - yp_token_t name; - struct yp_node *value; -} yp_keyword_parameter_node_t; - -// KeywordRestParameterNode -typedef struct yp_keyword_rest_parameter_node { - yp_node_t base; - yp_token_t operator; - yp_token_t name; -} yp_keyword_rest_parameter_node_t; - -// LambdaNode -typedef struct yp_lambda_node { - yp_node_t base; - yp_token_list_t locals; - yp_token_t opening; - struct yp_block_parameters_node *parameters; - struct yp_node *statements; -} yp_lambda_node_t; - -// LocalVariableReadNode -typedef struct yp_local_variable_read_node { - yp_node_t base; - uint32_t depth; -} yp_local_variable_read_node_t; - -// LocalVariableWriteNode -typedef struct yp_local_variable_write_node { - yp_node_t base; - yp_location_t name_loc; - struct yp_node *value; - yp_location_t operator_loc; - uint32_t depth; -} yp_local_variable_write_node_t; - -// MatchPredicateNode -typedef struct yp_match_predicate_node { - yp_node_t base; - struct yp_node *value; - struct yp_node *pattern; - yp_location_t operator_loc; -} yp_match_predicate_node_t; - -// MatchRequiredNode -typedef struct yp_match_required_node { - yp_node_t base; - struct yp_node *value; - struct yp_node *pattern; - yp_location_t operator_loc; -} yp_match_required_node_t; - -// MissingNode -typedef struct yp_missing_node { - yp_node_t base; -} yp_missing_node_t; - -// ModuleNode -typedef struct yp_module_node { - yp_node_t base; - yp_token_list_t locals; - yp_token_t module_keyword; - struct yp_node *constant_path; - struct yp_node *statements; - yp_token_t end_keyword; -} yp_module_node_t; - -// MultiWriteNode -typedef struct yp_multi_write_node { - yp_node_t base; - struct yp_node_list targets; - yp_token_t operator; - struct yp_node *value; - yp_location_t lparen_loc; - yp_location_t rparen_loc; -} yp_multi_write_node_t; - -// NextNode -typedef struct yp_next_node { - yp_node_t base; - struct yp_arguments_node *arguments; - yp_location_t keyword_loc; -} yp_next_node_t; - -// NilNode -typedef struct yp_nil_node { - yp_node_t base; -} yp_nil_node_t; - -// NoKeywordsParameterNode -typedef struct yp_no_keywords_parameter_node { - yp_node_t base; - yp_location_t operator_loc; - yp_location_t keyword_loc; -} yp_no_keywords_parameter_node_t; - -// OperatorAndAssignmentNode -typedef struct yp_operator_and_assignment_node { - yp_node_t base; - struct yp_node *target; - struct yp_node *value; - yp_location_t operator_loc; -} yp_operator_and_assignment_node_t; - -// OperatorAssignmentNode -typedef struct yp_operator_assignment_node { - yp_node_t base; - struct yp_node *target; - yp_token_t operator; - struct yp_node *value; -} yp_operator_assignment_node_t; - -// OperatorOrAssignmentNode -typedef struct yp_operator_or_assignment_node { - yp_node_t base; - struct yp_node *target; - struct yp_node *value; - yp_location_t operator_loc; -} yp_operator_or_assignment_node_t; - -// OptionalParameterNode -typedef struct yp_optional_parameter_node { - yp_node_t base; - yp_token_t name; - yp_token_t equal_operator; - struct yp_node *value; -} yp_optional_parameter_node_t; - -// OrNode -typedef struct yp_or_node { - yp_node_t base; - struct yp_node *left; - struct yp_node *right; - yp_location_t operator_loc; -} yp_or_node_t; - -// ParametersNode -typedef struct yp_parameters_node { - yp_node_t base; - struct yp_node_list requireds; - struct yp_node_list optionals; - struct yp_node_list posts; - struct yp_rest_parameter_node *rest; - struct yp_node_list keywords; - struct yp_node *keyword_rest; - struct yp_block_parameter_node *block; -} yp_parameters_node_t; - -// ParenthesesNode -typedef struct yp_parentheses_node { - yp_node_t base; - struct yp_node *statements; - yp_location_t opening_loc; - yp_location_t closing_loc; -} yp_parentheses_node_t; - -// PinnedExpressionNode -typedef struct yp_pinned_expression_node { - yp_node_t base; - struct yp_node *expression; - yp_location_t operator_loc; - yp_location_t lparen_loc; - yp_location_t rparen_loc; -} yp_pinned_expression_node_t; - -// PinnedVariableNode -typedef struct yp_pinned_variable_node { - yp_node_t base; - struct yp_node *variable; - yp_location_t operator_loc; -} yp_pinned_variable_node_t; - -// PostExecutionNode -typedef struct yp_post_execution_node { - yp_node_t base; - struct yp_statements_node *statements; - yp_location_t keyword_loc; - yp_location_t opening_loc; - yp_location_t closing_loc; -} yp_post_execution_node_t; - -// PreExecutionNode -typedef struct yp_pre_execution_node { - yp_node_t base; - struct yp_statements_node *statements; - yp_location_t keyword_loc; - yp_location_t opening_loc; - yp_location_t closing_loc; -} yp_pre_execution_node_t; - -// ProgramNode -typedef struct yp_program_node { - yp_node_t base; - yp_token_list_t locals; - struct yp_statements_node *statements; -} yp_program_node_t; - -// RangeNode -typedef struct yp_range_node { - yp_node_t base; - struct yp_node *left; - struct yp_node *right; - yp_location_t operator_loc; -} yp_range_node_t; - -// RationalNode -typedef struct yp_rational_node { - yp_node_t base; - struct yp_node *numeric; -} yp_rational_node_t; - -// RedoNode -typedef struct yp_redo_node { - yp_node_t base; -} yp_redo_node_t; - -// RegularExpressionNode -typedef struct yp_regular_expression_node { - yp_node_t base; - yp_token_t opening; - yp_token_t content; - yp_token_t closing; - yp_string_t unescaped; -} yp_regular_expression_node_t; - -// RequiredDestructuredParameterNode -typedef struct yp_required_destructured_parameter_node { - yp_node_t base; - struct yp_node_list parameters; - yp_token_t opening; - yp_token_t closing; -} yp_required_destructured_parameter_node_t; - -// RequiredParameterNode -typedef struct yp_required_parameter_node { - yp_node_t base; -} yp_required_parameter_node_t; - -// RescueModifierNode -typedef struct yp_rescue_modifier_node { - yp_node_t base; - struct yp_node *expression; - yp_token_t rescue_keyword; - struct yp_node *rescue_expression; -} yp_rescue_modifier_node_t; - -// RescueNode -typedef struct yp_rescue_node { - yp_node_t base; - yp_token_t rescue_keyword; - struct yp_node_list exceptions; - yp_token_t equal_greater; - struct yp_node *exception; - struct yp_statements_node *statements; - struct yp_rescue_node *consequent; -} yp_rescue_node_t; - -// RestParameterNode -typedef struct yp_rest_parameter_node { - yp_node_t base; - yp_token_t operator; - yp_token_t name; -} yp_rest_parameter_node_t; - -// RetryNode -typedef struct yp_retry_node { - yp_node_t base; -} yp_retry_node_t; - -// ReturnNode -typedef struct yp_return_node { - yp_node_t base; - yp_token_t keyword; - struct yp_arguments_node *arguments; -} yp_return_node_t; - -// SelfNode -typedef struct yp_self_node { - yp_node_t base; -} yp_self_node_t; - -// SingletonClassNode -typedef struct yp_singleton_class_node { - yp_node_t base; - yp_token_list_t locals; - yp_token_t class_keyword; - yp_token_t operator; - struct yp_node *expression; - struct yp_node *statements; - yp_token_t end_keyword; -} yp_singleton_class_node_t; - -// SourceEncodingNode -typedef struct yp_source_encoding_node { - yp_node_t base; -} yp_source_encoding_node_t; - -// SourceFileNode -typedef struct yp_source_file_node { - yp_node_t base; - yp_string_t filepath; -} yp_source_file_node_t; - -// SourceLineNode -typedef struct yp_source_line_node { - yp_node_t base; -} yp_source_line_node_t; - -// SplatNode -typedef struct yp_splat_node { - yp_node_t base; - yp_token_t operator; - struct yp_node *expression; -} yp_splat_node_t; - -// StatementsNode -typedef struct yp_statements_node { - yp_node_t base; - struct yp_node_list body; -} yp_statements_node_t; - -// StringConcatNode -typedef struct yp_string_concat_node { - yp_node_t base; - struct yp_node *left; - struct yp_node *right; -} yp_string_concat_node_t; - -// StringInterpolatedNode -typedef struct yp_string_interpolated_node { - yp_node_t base; - yp_token_t opening; - struct yp_statements_node *statements; - yp_token_t closing; -} yp_string_interpolated_node_t; - -// StringNode -typedef struct yp_string_node { - yp_node_t base; - yp_token_t opening; - yp_token_t content; - yp_token_t closing; - yp_string_t unescaped; -} yp_string_node_t; - -// SuperNode -typedef struct yp_super_node { - yp_node_t base; - yp_token_t keyword; - yp_token_t lparen; - struct yp_arguments_node *arguments; - yp_token_t rparen; - struct yp_block_node *block; -} yp_super_node_t; - -// SymbolNode -typedef struct yp_symbol_node { - yp_node_t base; - yp_token_t opening; - yp_token_t value; - yp_token_t closing; - yp_string_t unescaped; -} yp_symbol_node_t; - -// TrueNode -typedef struct yp_true_node { - yp_node_t base; -} yp_true_node_t; - -// UndefNode -typedef struct yp_undef_node { - yp_node_t base; - struct yp_node_list names; - yp_location_t keyword_loc; -} yp_undef_node_t; - -// UnlessNode -typedef struct yp_unless_node { - yp_node_t base; - yp_token_t keyword; - struct yp_node *predicate; - struct yp_statements_node *statements; - struct yp_else_node *consequent; - yp_token_t end_keyword; -} yp_unless_node_t; - -// UntilNode -typedef struct yp_until_node { - yp_node_t base; - yp_token_t keyword; - struct yp_node *predicate; - struct yp_statements_node *statements; -} yp_until_node_t; - -// WhenNode -typedef struct yp_when_node { - yp_node_t base; - yp_token_t when_keyword; - struct yp_node_list conditions; - struct yp_statements_node *statements; -} yp_when_node_t; - -// WhileNode -typedef struct yp_while_node { - yp_node_t base; - yp_token_t keyword; - struct yp_node *predicate; - struct yp_statements_node *statements; -} yp_while_node_t; - -// XStringNode -typedef struct yp_x_string_node { - yp_node_t base; - yp_token_t opening; - yp_token_t content; - yp_token_t closing; - yp_string_t unescaped; -} yp_x_string_node_t; - -// YieldNode -typedef struct yp_yield_node { - yp_node_t base; - yp_token_t keyword; - yp_token_t lparen; - struct yp_arguments_node *arguments; - yp_token_t rparen; -} yp_yield_node_t; - -#endif // YARP_AST_H diff --git a/src/main/c/yarp/src/yarp/include/yarp/defines.h b/src/main/c/yarp/src/yarp/include/yarp/defines.h deleted file mode 100644 index db6b1ab3c32a..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp/defines.h +++ /dev/null @@ -1,12 +0,0 @@ -// This files must be required before any system header -// as it influences which functions system headers declare. - -#ifndef YARP_DEFINES_H -#define YARP_DEFINES_H - -// For strnlen(), strncasecmp() -#ifndef _XOPEN_SOURCE -#define _XOPEN_SOURCE 700 -#endif - -#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/enc/yp_encoding.h b/src/main/c/yarp/src/yarp/include/yarp/enc/yp_encoding.h deleted file mode 100644 index 505a7e8e74b9..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp/enc/yp_encoding.h +++ /dev/null @@ -1,388 +0,0 @@ -#ifndef YARP_ENCODING_H -#define YARP_ENCODING_H - -#include "yarp/defines.h" - -#include -#include -#include - -#define YP_ENCODING_ALPHABETIC_BIT 1 << 0 -#define YP_ENCODING_ALPHANUMERIC_BIT 1 << 1 -#define YP_ENCODING_UPPERCASE_BIT 1 << 2 - -// The function is shared between all of the encodings that use single bytes to -// represent characters. They don't have need of a dynamic function to determine -// their width. -size_t -yp_encoding_single_char_width(__attribute__((unused)) const char *c); - -/******************************************************************************/ -/* ASCII */ -/******************************************************************************/ - -size_t -yp_encoding_ascii_char_width(const char *c); - -size_t -yp_encoding_ascii_alpha_char(const char *c); - -size_t -yp_encoding_ascii_alnum_char(const char *c); - -bool -yp_encoding_ascii_isupper_char(const char *c); - -/******************************************************************************/ -/* Big5 */ -/******************************************************************************/ - -size_t -yp_encoding_big5_char_width(const char *c); - -size_t -yp_encoding_big5_alpha_char(const char *c); - -size_t -yp_encoding_big5_alnum_char(const char *c); - -bool -yp_encoding_big5_isupper_char(const char *c); - -/******************************************************************************/ -/* EUC-JP */ -/******************************************************************************/ - -size_t -yp_encoding_euc_jp_char_width(const char *c); - -size_t -yp_encoding_euc_jp_alpha_char(const char *c); - -size_t -yp_encoding_euc_jp_alnum_char(const char *c); - -bool -yp_encoding_euc_jp_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-1 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_1_char_width(const char *c); - -size_t -yp_encoding_iso_8859_1_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_1_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_1_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-2 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_2_char_width(const char *c); - -size_t -yp_encoding_iso_8859_2_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_2_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_2_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-3 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_3_char_width(const char *c); - -size_t -yp_encoding_iso_8859_3_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_3_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_3_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-4 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_4_char_width(const char *c); - -size_t -yp_encoding_iso_8859_4_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_4_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_4_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-5 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_5_char_width(const char *c); - -size_t -yp_encoding_iso_8859_5_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_5_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_5_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-6 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_6_char_width(const char *c); - -size_t -yp_encoding_iso_8859_6_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_6_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_6_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-7 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_7_char_width(const char *c); - -size_t -yp_encoding_iso_8859_7_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_7_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_7_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-8 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_8_char_width(const char *c); - -size_t -yp_encoding_iso_8859_8_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_8_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_8_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-9 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_9_char_width(const char *c); - -size_t -yp_encoding_iso_8859_9_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_9_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_9_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-10 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_10_char_width(const char *c); - -size_t -yp_encoding_iso_8859_10_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_10_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_10_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-11 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_11_char_width(const char *c); - -size_t -yp_encoding_iso_8859_11_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_11_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_11_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-13 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_13_char_width(const char *c); - -size_t -yp_encoding_iso_8859_13_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_13_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_13_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-14 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_14_char_width(const char *c); - -size_t -yp_encoding_iso_8859_14_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_14_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_14_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-15 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_15_char_width(const char *c); - -size_t -yp_encoding_iso_8859_15_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_15_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_15_isupper_char(const char *c); - -/******************************************************************************/ -/* ISO-8859-16 */ -/******************************************************************************/ - -size_t -yp_encoding_iso_8859_16_char_width(const char *c); - -size_t -yp_encoding_iso_8859_16_alpha_char(const char *c); - -size_t -yp_encoding_iso_8859_16_alnum_char(const char *c); - -bool -yp_encoding_iso_8859_16_isupper_char(const char *c); - -/******************************************************************************/ -/* Shift-JIS */ -/******************************************************************************/ - -size_t -yp_encoding_shift_jis_char_width(const char *c); - -size_t -yp_encoding_shift_jis_alpha_char(const char *c); - -size_t -yp_encoding_shift_jis_alnum_char(const char *c); - -bool -yp_encoding_shift_jis_isupper_char(const char *c); - -/******************************************************************************/ -/* UTF-8 */ -/******************************************************************************/ - -size_t -yp_encoding_utf_8_char_width(const char *c); - -size_t -yp_encoding_utf_8_alpha_char(const char *c); - -size_t -yp_encoding_utf_8_alnum_char(const char *c); - -bool -yp_encoding_utf_8_isupper_char(const char *c); - -/******************************************************************************/ -/* Windows-31J */ -/******************************************************************************/ - -size_t -yp_encoding_windows_31j_char_width(const char *c); - -size_t -yp_encoding_windows_31j_alpha_char(const char *c); - -size_t -yp_encoding_windows_31j_alnum_char(const char *c); - -bool -yp_encoding_windows_31j_isupper_char(const char *c); - -/******************************************************************************/ -/* Windows-1251 */ -/******************************************************************************/ - -size_t -yp_encoding_windows_1251_char_width(const char *c); - -size_t -yp_encoding_windows_1251_alpha_char(const char *c); - -size_t -yp_encoding_windows_1251_alnum_char(const char *c); - -bool -yp_encoding_windows_1251_isupper_char(const char *c); - -/******************************************************************************/ -/* Windows-1252 */ -/******************************************************************************/ - -size_t -yp_encoding_windows_1252_char_width(const char *c); - -size_t -yp_encoding_windows_1252_alpha_char(const char *c); - -size_t -yp_encoding_windows_1252_alnum_char(const char *c); - -bool -yp_encoding_windows_1252_isupper_char(const char *c); - -#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/missing.h b/src/main/c/yarp/src/yarp/include/yarp/missing.h deleted file mode 100644 index da04a28f655b..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp/missing.h +++ /dev/null @@ -1,24 +0,0 @@ -#ifndef YARP_MISSING_H -#define YARP_MISSING_H - -#include "yarp/defines.h" - -#include -#include -#include - -const char * -yp_strnstr(const char *haystack, const char *needle, size_t length); - -#ifndef HAVE_STRNSTR -#define strnstr yp_strnstr -#endif - -int -yp_strncasecmp(const char *string1, const char *string2, size_t length); - -#ifndef HAVE_STRNCASECMP -#define strncasecmp yp_strncasecmp -#endif - -#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/node.h b/src/main/c/yarp/src/yarp/include/yarp/node.h deleted file mode 100644 index 8eac195d1f5c..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp/node.h +++ /dev/null @@ -1,37 +0,0 @@ -#ifndef YARP_NODE_H -#define YARP_NODE_H - -#include "yarp/defines.h" - -#include "yarp.h" -#include "yarp/parser.h" - -// Initialize a yp_token_list_t with its default values. -void -yp_token_list_init(yp_token_list_t *token_list); - -// Append a token to the given list. -void -yp_token_list_append(yp_token_list_t *token_list, const yp_token_t *token); - -// Checks if the current token list includes the given token. -bool -yp_token_list_includes(yp_token_list_t *token_list, const yp_token_t *token); - -// Initiailize a list of nodes. -void -yp_node_list_init(yp_node_list_t *node_list); - -// Append a new node onto the end of the node list. -void -yp_node_list_append(yp_node_list_t *list, yp_node_t *node); - -// Clear the node but preserves the location. -void -yp_node_clear(yp_node_t *node); - -#define YP_EMPTY_NODE_LIST { .nodes = NULL, .size = 0, .capacity = 0 } - -#define YP_EMPTY_TOKEN_LIST { .tokens = NULL, .size = 0, .capacity = 0 } - -#endif // YARP_NODE_H diff --git a/src/main/c/yarp/src/yarp/include/yarp/parser.h b/src/main/c/yarp/src/yarp/include/yarp/parser.h deleted file mode 100644 index b7c49a2e1c6e..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp/parser.h +++ /dev/null @@ -1,384 +0,0 @@ -#ifndef YARP_PARSER_H -#define YARP_PARSER_H - -#include "yarp/defines.h" - -#include - -#include "yarp/ast.h" -#include "yarp/enc/yp_encoding.h" -#include "yarp/util/yp_list.h" -#include "yarp/util/yp_state_stack.h" - -// This enum provides various bits that represent different kinds of states that -// the lexer can track. This is used to determine which kind of token to return -// based on the context of the parser. -typedef enum { - YP_LEX_STATE_BIT_BEG, - YP_LEX_STATE_BIT_END, - YP_LEX_STATE_BIT_ENDARG, - YP_LEX_STATE_BIT_ENDFN, - YP_LEX_STATE_BIT_ARG, - YP_LEX_STATE_BIT_CMDARG, - YP_LEX_STATE_BIT_MID, - YP_LEX_STATE_BIT_FNAME, - YP_LEX_STATE_BIT_DOT, - YP_LEX_STATE_BIT_CLASS, - YP_LEX_STATE_BIT_LABEL, - YP_LEX_STATE_BIT_LABELED, - YP_LEX_STATE_BIT_FITEM -} yp_lex_state_bit_t; - -// This enum combines the various bits from the above enum into individual -// values that represent the various states of the lexer. -typedef enum { - YP_LEX_STATE_NONE = 0, - YP_LEX_STATE_BEG = (1 << YP_LEX_STATE_BIT_BEG), - YP_LEX_STATE_END = (1 << YP_LEX_STATE_BIT_END), - YP_LEX_STATE_ENDARG = (1 << YP_LEX_STATE_BIT_ENDARG), - YP_LEX_STATE_ENDFN = (1 << YP_LEX_STATE_BIT_ENDFN), - YP_LEX_STATE_ARG = (1 << YP_LEX_STATE_BIT_ARG), - YP_LEX_STATE_CMDARG = (1 << YP_LEX_STATE_BIT_CMDARG), - YP_LEX_STATE_MID = (1 << YP_LEX_STATE_BIT_MID), - YP_LEX_STATE_FNAME = (1 << YP_LEX_STATE_BIT_FNAME), - YP_LEX_STATE_DOT = (1 << YP_LEX_STATE_BIT_DOT), - YP_LEX_STATE_CLASS = (1 << YP_LEX_STATE_BIT_CLASS), - YP_LEX_STATE_LABEL = (1 << YP_LEX_STATE_BIT_LABEL), - YP_LEX_STATE_LABELED = (1 << YP_LEX_STATE_BIT_LABELED), - YP_LEX_STATE_FITEM = (1 << YP_LEX_STATE_BIT_FITEM), - YP_LEX_STATE_BEG_ANY = YP_LEX_STATE_BEG | YP_LEX_STATE_MID | YP_LEX_STATE_CLASS, - YP_LEX_STATE_ARG_ANY = YP_LEX_STATE_ARG | YP_LEX_STATE_CMDARG, - YP_LEX_STATE_END_ANY = YP_LEX_STATE_END | YP_LEX_STATE_ENDARG | YP_LEX_STATE_ENDFN -} yp_lex_state_t; - -typedef enum { - YP_HEREDOC_QUOTE_NONE, - YP_HEREDOC_QUOTE_SINGLE = '\'', - YP_HEREDOC_QUOTE_DOUBLE = '"', - YP_HEREDOC_QUOTE_BACKTICK = '`', -} yp_heredoc_quote_t; - -typedef enum { - YP_HEREDOC_INDENT_NONE, - YP_HEREDOC_INDENT_DASH, - YP_HEREDOC_INDENT_TILDE, -} yp_heredoc_indent_t; - -// When lexing Ruby source, the lexer has a small amount of state to tell which -// kind of token it is currently lexing. For example, when we find the start of -// a string, the first token that we return is a TOKEN_STRING_BEGIN token. After -// that the lexer is now in the YP_LEX_STRING mode, and will return tokens that -// are found as part of a string. -typedef struct yp_lex_mode { - enum { - // This state is used when any given token is being lexed. - YP_LEX_DEFAULT, - - // This state is used when we're lexing as normal but inside an embedded - // expression of a string. - YP_LEX_EMBEXPR, - - // This state is used when we're lexing a variable that is embedded directly - // inside of a string with the # shorthand. - YP_LEX_EMBVAR, - - // This state is used when you are inside the content of a heredoc. - YP_LEX_HEREDOC, - - // This state is used when we are lexing a list of tokens, as in a %w word - // list literal or a %i symbol list literal. - YP_LEX_LIST, - - // This state is used when a regular expression has been begun and we are - // looking for the terminator. - YP_LEX_REGEXP, - - // This state is used when we are lexing a string or a string-like token, as - // in string content with either quote or an xstring. - YP_LEX_STRING, - - // you lexed a number with extra information attached - YP_LEX_NUMERIC, - } mode; - - union { - struct { - // When lexing a list, it takes into account balancing the terminator if - // the terminator is one of (), [], {}, or <>. - char incrementor; - - // This is the terminator of the list literal. - char terminator; - - // This keeps track of the nesting level of the list. - size_t nesting; - - // Whether or not interpolation is allowed in this list. - bool interpolation; - } list; - - struct { - // When lexing a regular expression, it takes into account balancing the - // terminator if the terminator is one of (), [], {}, or <>. - char incrementor; - - // This is the terminator of the regular expression. - char terminator; - - // This keeps track of the nesting level of the regular expression. - size_t nesting; - } regexp; - - struct { - // When lexing a string, it takes into account balancing the terminator if - // the terminator is one of (), [], {}, or <>. - char incrementor; - - // This is the terminator of the string. It is typically either a single - // or double quote. - char terminator; - - // This keeps track of the nesting level of the string. - size_t nesting; - - // Whether or not interpolation is allowed in this string. - bool interpolation; - - // Whether or not at the end of the string we should allow a :, which - // would indicate this was a dynamic symbol instead of a string. - bool label_allowed; - } string; - - struct { - yp_token_type_t type; - const char *start; - const char *end; - } numeric; - - struct { - // These pointers point to the beginning and end of the heredoc - // identifier. - const char *ident_start; - size_t ident_length; - - yp_heredoc_quote_t quote; - yp_heredoc_indent_t indent; - - // This is the pointer to the character where lexing should resume once - // the heredoc has been completely processed. - const char *next_start; - } heredoc; - } as; - - // The previous lex state so that it knows how to pop. - struct yp_lex_mode *prev; -} yp_lex_mode_t; - -// We pre-allocate a certain number of lex states in order to avoid having to -// call malloc too many times while parsing. You really shouldn't need more than -// this because you only really nest deeply when doing string interpolation. -#define YP_LEX_STACK_SIZE 4 - -// A forward declaration since our error handler struct accepts a parser for -// each of its function calls. -typedef struct yp_parser yp_parser_t; - -// While parsing, we keep track of a stack of contexts. This is helpful for -// error recovery so that we can pop back to a previous context when we hit a -// token that is understood by a parent context but not by the current context. -typedef enum { - YP_CONTEXT_BEGIN, // a begin statement - YP_CONTEXT_BLOCK_BRACES, // expressions in block arguments using braces - YP_CONTEXT_BLOCK_KEYWORDS, // expressions in block arguments using do..end - YP_CONTEXT_CASE_WHEN, // a case when statements - YP_CONTEXT_CASE_IN, // a case in statements - YP_CONTEXT_CLASS, // a class declaration - YP_CONTEXT_DEF, // a method definition - YP_CONTEXT_DEF_PARAMS, // a method definition's parameters - YP_CONTEXT_DEFAULT_PARAMS, // a method definition's default parameter - YP_CONTEXT_ELSE, // an else clause - YP_CONTEXT_ELSIF, // an elsif clause - YP_CONTEXT_EMBEXPR, // an interpolated expression - YP_CONTEXT_ENSURE, // an ensure statement - YP_CONTEXT_FOR, // a for loop - YP_CONTEXT_IF, // an if statement - YP_CONTEXT_LAMBDA_BRACES, // a lambda expression with braces - YP_CONTEXT_LAMBDA_DO_END, // a lambda expression with do..end - YP_CONTEXT_MAIN, // the top level context - YP_CONTEXT_MODULE, // a module declaration - YP_CONTEXT_PARENS, // a parenthesized expression - YP_CONTEXT_POSTEXE, // an END block - YP_CONTEXT_PREDICATE, // a predicate inside an if/elsif/unless statement - YP_CONTEXT_PREEXE, // a BEGIN block - YP_CONTEXT_RESCUE_ELSE, // a rescue else statement - YP_CONTEXT_RESCUE, // a rescue statement - YP_CONTEXT_SCLASS, // a singleton class definition - YP_CONTEXT_UNLESS, // an unless statement - YP_CONTEXT_UNTIL, // an until statement - YP_CONTEXT_WHILE, // a while statement -} yp_context_t; - -// This is a node in a linked list of contexts. -typedef struct yp_context_node { - yp_context_t context; - struct yp_context_node *prev; -} yp_context_node_t; - -// This is the type of a comment that we've found while parsing. -typedef enum { - YP_COMMENT_INLINE, - YP_COMMENT_EMBDOC, - YP_COMMENT___END__ -} yp_comment_type_t; - -// This is a node in the linked list of comments that we've found while parsing. -typedef struct yp_comment { - yp_list_node_t node; - const char *start; - const char *end; - yp_comment_type_t type; -} yp_comment_t; - -// This struct defines the functions necessary to implement the encoding -// interface so we can determine how many bytes the subsequent character takes. -// Each callback should return the number of bytes, or 0 if the next bytes are -// invalid for the encoding and type. -typedef struct { - const char *name; - size_t (*char_width)(const char *c); - size_t (*alpha_char)(const char *c); - size_t (*alnum_char)(const char *c); - bool (*isupper_char)(const char *c); -} yp_encoding_t; - -// When the encoding that is being used to parse the source is changed by YARP, -// we provide the ability here to call out to a user-defined function. -typedef void (*yp_encoding_changed_callback_t)(yp_parser_t *parser); - -// When an encoding is encountered that isn't understood by YARP, we provide -// the ability here to call out to a user-defined function to get an encoding -// struct. If the function returns something that isn't NULL, we set that to -// our encoding and use it to parse identifiers. -typedef yp_encoding_t *(*yp_encoding_decode_callback_t)(yp_parser_t *parser, const char *name, size_t width); - -// When you are lexing through a file, the lexer needs all of the information -// that the parser additionally provides (for example, the local table). So if -// you want to properly lex Ruby, you need to actually lex it in the context of -// the parser. In order to provide this functionality, we optionally allow a -// struct to be attached to the parser that calls back out to a user-provided -// callback when each token is lexed. -typedef struct { - // This opaque pointer is used to provide whatever information the user deemed - // necessary to the callback. In our case we use it to pass the array that the - // tokens get appended into. - void *data; - - // This is the callback that is called when a token is lexed. It is passed the - // opaque data pointer, the parser, and the token that was lexed. - void (*callback)(void *data, yp_parser_t *parser, yp_token_t *token); -} yp_lex_callback_t; - -// This struct represents a node in a linked list of scopes. Some scopes can see -// into their parent scopes, while others cannot. -typedef struct yp_scope { - // Locals in the given scope. - yp_token_list_t locals; - - // A boolean indicating whether or not this scope can see into its parent. If - // top is true, then the scope cannot see into its parent. - bool top; - - // A pointer to the previous scope in the linked list. - struct yp_scope *previous; -} yp_scope_t; - -// This struct represents the overall parser. It contains a reference to the -// source file, as well as pointers that indicate where in the source it's -// currently parsing. It also contains the most recent and current token that -// it's considering. -struct yp_parser { - yp_lex_state_t lex_state; // the current state of the lexer - bool command_start; // whether or not we're at the beginning of a command - int enclosure_nesting; // tracks the current nesting of (), [], and {} - - // Used to temporarily track the nesting of enclosures to determine if a { is - // the beginning of a lambda following the parameters of a lambda. - int lambda_enclosure_nesting; - - // Used to track the nesting of braces to ensure we get the correct value when - // we are interpolating blocks with braces. - int brace_nesting; - - // the stack used to determine if a do keyword belongs to the predicate of a - // while, until, or for loop - yp_state_stack_t do_loop_stack; - - // the stack used to determine if a do keyword belongs to the beginning of a - // block - yp_state_stack_t accepts_block_stack; - - struct { - yp_lex_mode_t *current; // the current mode of the lexer - yp_lex_mode_t stack[YP_LEX_STACK_SIZE]; // the stack of lexer modes - size_t index; // the current index into the lexer mode stack - } lex_modes; - - const char *start; // the pointer to the start of the source - const char *end; // the pointer to the end of the source - yp_token_t previous; // the previous token we were considering - yp_token_t current; // the current token we're considering - - // This is a special field set on the parser when we need the parser to jump - // to a specific location when lexing the next token, as opposed to just using - // the end of the previous token. Normally this is NULL. - const char *next_start; - - // This field indicates the end of a heredoc whose identifier was found on the - // current line. If another heredoc is found on the same line, then this will - // be moved forward to the end of that heredoc. If no heredocs are found on a - // line then this is NULL. - const char *heredoc_end; - - yp_list_t comment_list; // the list of comments that have been found while parsing - yp_list_t warning_list; // the list of warnings that have been found while parsing - yp_list_t error_list; // the list of errors that have been found while parsing - yp_scope_t *current_scope; // the current local scope - - yp_context_node_t *current_context; // the current parsing context - bool recovering; // whether or not we're currently recovering from a syntax error - - // The encoding functions for the current file is attached to the parser as - // it's parsing so that it can change with a magic comment. - yp_encoding_t encoding; - - // When the encoding that is being used to parse the source is changed by - // YARP, we provide the ability here to call out to a user-defined function. - yp_encoding_changed_callback_t encoding_changed_callback; - - // When an encoding is encountered that isn't understood by YARP, we provide - // the ability here to call out to a user-defined function to get an encoding - // struct. If the function returns something that isn't NULL, we set that to - // our encoding and use it to parse identifiers. - yp_encoding_decode_callback_t encoding_decode_callback; - - // This pointer indicates where a comment must start if it is to be considered - // an encoding comment. - const char *encoding_comment_start; - - // This is an optional callback that can be attached to the parser that will - // be called whenever a new token is lexed by the parser. - yp_lex_callback_t *lex_callback; - - // This flag indicates that we are currently parsing a pattern matching - // expression and impacts that calculation of newlines. - bool pattern_matching_newlines; - - // This flag indicates that we are currently parsing a keyword argument. - bool in_keyword_arg; - - // This is the path of the file being parsed - // We use the filepath when constructing SourceFileNodes - yp_string_t filepath_string; -}; - -#endif // YARP_PARSER_H diff --git a/src/main/c/yarp/src/yarp/include/yarp/unescape.h b/src/main/c/yarp/src/yarp/include/yarp/unescape.h deleted file mode 100644 index db31e6093149..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp/unescape.h +++ /dev/null @@ -1,39 +0,0 @@ -#ifndef YARP_UNESCAPE_H -#define YARP_UNESCAPE_H - -#include "yarp/defines.h" - -#include -#include -#include -#include - -#include "yarp/diagnostic.h" -#include "yarp/util/yp_char.h" -#include "yarp/util/yp_list.h" -#include "yarp/util/yp_string.h" - -// The type of unescape we are performing. -typedef enum { - // When we're creating a string inside of a list literal like %w, we shouldn't - // escape anything. - YP_UNESCAPE_NONE, - - // When we're unescaping a single-quoted string, we only need to unescape - // single quotes and backslashes. - YP_UNESCAPE_MINIMAL, - - // When we're unescaping a double-quoted string, we need to unescape all - // escapes. - YP_UNESCAPE_ALL -} yp_unescape_type_t; - -// Unescape the contents of the given token into the given string using the -// given unescape mode. -__attribute__((__visibility__("default"))) extern void -yp_unescape_manipulate_string(const char *value, size_t length, yp_string_t *string, yp_unescape_type_t unescape_type, yp_list_t *error_list); - -__attribute__((__visibility__("default"))) extern size_t -yp_unescape_calculate_difference(const char *value, const char *end, yp_unescape_type_t unescape_type, bool expect_single_codepoint, yp_list_t *error_list); - -#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/util/yp_conversion.h b/src/main/c/yarp/src/yarp/include/yarp/util/yp_conversion.h deleted file mode 100644 index 4e49ef5fa80b..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp/util/yp_conversion.h +++ /dev/null @@ -1,18 +0,0 @@ -#ifndef YARP_CONVERSION_H -#define YARP_CONVERSION_H - -// This file is responsible for defining functions that we use internally to -// convert between various widths safely without overflow. - -#include "yarp/defines.h" - -#include -#include - -uint32_t -yp_long_to_u32(long value); - -uint32_t -yp_ulong_to_u32(unsigned long value); - -#endif diff --git a/src/main/c/yarp/src/yarp/include/yarp/util/yp_string.h b/src/main/c/yarp/src/yarp/include/yarp/util/yp_string.h deleted file mode 100644 index f48c3c352f84..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp/util/yp_string.h +++ /dev/null @@ -1,69 +0,0 @@ -#ifndef YARP_STRING_H -#define YARP_STRING_H - -#include "yarp/defines.h" - -#include -#include -#include - -// This struct represents a string value. -typedef struct { - enum { YP_STRING_SHARED, YP_STRING_OWNED, YP_STRING_CONSTANT } type; - - union { - struct { - const char *start; - const char *end; - } shared; - - struct { - char *source; - size_t length; - } owned; - - struct { - const char *source; - size_t length; - } constant; - } as; -} yp_string_t; - -// Allocate a new yp_string_t. -yp_string_t * -yp_string_alloc(void); - -// Initialize a shared string that is based on initial input. -void -yp_string_shared_init(yp_string_t *string, const char *start, const char *end); - -// Initialize an owned string that is responsible for freeing allocated memory. -void -yp_string_owned_init(yp_string_t *string, char *source, size_t length); - -// Initialize a constant string that doesn't own its memory source. -void -yp_string_constant_init(yp_string_t *string, const char *source, size_t length); - -// Returns the memory size associated with the string. -size_t -yp_string_memsize(const yp_string_t *string); - -// Ensure the string is owned. If it is not, then reinitialize it as owned and -// copy over the previous source. -void -yp_string_ensure_owned(yp_string_t *string); - -// Returns the length associated with the string. -__attribute__ ((__visibility__("default"))) extern size_t -yp_string_length(const yp_string_t *string); - -// Returns the start pointer associated with the string. -__attribute__ ((__visibility__("default"))) extern const char * -yp_string_source(const yp_string_t *string); - -// Free the associated memory of the given string. -__attribute__((__visibility__("default"))) extern void -yp_string_free(yp_string_t *string); - -#endif // YARP_STRING_H diff --git a/src/main/c/yarp/src/yarp/include/yarp/util/yp_string_list.h b/src/main/c/yarp/src/yarp/include/yarp/util/yp_string_list.h deleted file mode 100644 index 2fac2b4e1ec6..000000000000 --- a/src/main/c/yarp/src/yarp/include/yarp/util/yp_string_list.h +++ /dev/null @@ -1,33 +0,0 @@ -#ifndef YARP_STRING_LIST_H -#define YARP_STRING_LIST_H - -#include "yarp/defines.h" - -#include -#include - -#include "yarp/util/yp_string.h" - -typedef struct { - yp_string_t *strings; - size_t length; - size_t capacity; -} yp_string_list_t; - -// Allocate a new yp_string_list_t. -yp_string_list_t * -yp_string_list_alloc(void); - -// Initialize a yp_string_list_t with its default values. -__attribute__((__visibility__("default"))) extern void -yp_string_list_init(yp_string_list_t *string_list); - -// Append a yp_string_t to the given string list. -void -yp_string_list_append(yp_string_list_t *string_list, yp_string_t *string); - -// Free the memory associated with the string list. -__attribute__((__visibility__("default"))) extern void -yp_string_list_free(yp_string_list_t *string_list); - -#endif diff --git a/src/main/c/yarp/src/yarp/src/diagnostic.c b/src/main/c/yarp/src/yarp/src/diagnostic.c deleted file mode 100644 index eef456186793..000000000000 --- a/src/main/c/yarp/src/yarp/src/diagnostic.c +++ /dev/null @@ -1,22 +0,0 @@ -#include "yarp/diagnostic.h" - -// Append an error to the given list of diagnostic. -void -yp_diagnostic_list_append(yp_list_t *list, const char *start, const char *end, const char *message) { - yp_diagnostic_t *diagnostic = (yp_diagnostic_t *) malloc(sizeof(yp_diagnostic_t)); - *diagnostic = (yp_diagnostic_t) { .start = start, .end = end, .message = message }; - yp_list_append(list, (yp_list_node_t *) diagnostic); -} - -// Deallocate the internal state of the given diagnostic list. -void -yp_diagnostic_list_free(yp_list_t *list) { - yp_list_node_t *node, *next; - - for (node = list->head; node != NULL; node = next) { - next = node->next; - - yp_diagnostic_t *diagnostic = (yp_diagnostic_t *) node; - free(diagnostic); - } -} diff --git a/src/main/c/yarp/src/yarp/src/enc/ascii.c b/src/main/c/yarp/src/yarp/src/enc/ascii.c deleted file mode 100644 index c6d9256a67b7..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/ascii.c +++ /dev/null @@ -1,47 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ASCII character. -static unsigned char yp_encoding_ascii_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx -}; - -size_t -yp_encoding_ascii_char_width(const char *c) { - const unsigned char v = (const unsigned char) *c; - return v < 128 ? 1 : 0; -} - -size_t -yp_encoding_ascii_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_ascii_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_ascii_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_ascii_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_ascii_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_ascii_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/big5.c b/src/main/c/yarp/src/yarp/src/enc/big5.c deleted file mode 100644 index f179aa264a8e..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/big5.c +++ /dev/null @@ -1,70 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -typedef uint32_t big5_codepoint_t; - -static big5_codepoint_t -big5_codepoint(const char *c, size_t *width) { - const unsigned char *uc = (const unsigned char *) c; - - // These are the single byte characters. - if (*uc < 0x80) { - *width = 1; - return *uc; - } - - // These are the double byte characters. - if ((uc[0] >= 0xA1 && uc[0] <= 0xFE) && (uc[1] >= 0x40 && uc[1] <= 0xFE)) { - *width = 2; - return (big5_codepoint_t) (uc[0] << 8 | uc[1]); - } - - *width = 0; - return 0; -} - -size_t -yp_encoding_big5_char_width(const char *c) { - size_t width; - big5_codepoint(c, &width); - - return width; -} - -size_t -yp_encoding_big5_alpha_char(const char *c) { - size_t width; - big5_codepoint_t codepoint = big5_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_alpha_char(&value); - } else { - return 0; - } -} - -size_t -yp_encoding_big5_alnum_char(const char *c) { - size_t width; - big5_codepoint_t codepoint = big5_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_alnum_char(&value); - } else { - return 0; - } -} - -bool -yp_encoding_big5_isupper_char(const char *c) { - size_t width; - big5_codepoint_t codepoint = big5_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_isupper_char(&value); - } else { - return false; - } -} diff --git a/src/main/c/yarp/src/yarp/src/enc/euc_jp.c b/src/main/c/yarp/src/yarp/src/enc/euc_jp.c deleted file mode 100644 index 9d19606cccfd..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/euc_jp.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -typedef uint32_t euc_jp_codepoint_t; - -static euc_jp_codepoint_t -euc_jp_codepoint(const char *c, size_t *width) { - const unsigned char *uc = (const unsigned char *) c; - - // These are the single byte characters. - if (*uc < 0x80) { - *width = 1; - return *uc; - } - - // These are the double byte characters. - if ( - ((uc[0] == 0x8E) && (uc[1] >= 0xA1 && uc[1] <= 0xFE)) || - ((uc[0] >= 0xA1 && uc[0] <= 0xFE) && (uc[1] >= 0xA1 && uc[1] <= 0xFE)) - ) { - *width = 2; - return (euc_jp_codepoint_t) (uc[0] << 8 | uc[1]); - } - - *width = 0; - return 0; -} - -size_t -yp_encoding_euc_jp_char_width(const char *c) { - size_t width; - euc_jp_codepoint(c, &width); - - return width; -} - -size_t -yp_encoding_euc_jp_alpha_char(const char *c) { - size_t width; - euc_jp_codepoint_t codepoint = euc_jp_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_alpha_char(&value); - } else { - return 0; - } -} - -size_t -yp_encoding_euc_jp_alnum_char(const char *c) { - size_t width; - euc_jp_codepoint_t codepoint = euc_jp_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_alnum_char(&value); - } else { - return 0; - } -} - -bool -yp_encoding_euc_jp_isupper_char(const char *c) { - size_t width; - euc_jp_codepoint_t codepoint = euc_jp_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_isupper_char(&value); - } else { - return 0; - } -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_1.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_1.c deleted file mode 100644 index 4f5762f9fad7..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_1.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-1 character. -static unsigned char yp_encoding_iso_8859_1_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax - 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx -}; - -size_t -yp_encoding_iso_8859_1_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_1_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_1_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_1_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_1_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_1_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_10.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_10.c deleted file mode 100644 index dfab142cc1cb..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_10.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-10 character. -static unsigned char yp_encoding_iso_8859_10_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 0, 7, 7, // Ax - 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 0, 3, 3, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx -}; - -size_t -yp_encoding_iso_8859_10_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_10_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_10_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_10_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_10_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_10_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_11.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_11.c deleted file mode 100644 index 9f6174f6f096..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_11.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-11 character. -static unsigned char yp_encoding_iso_8859_11_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ax - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Bx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, // Fx -}; - -size_t -yp_encoding_iso_8859_11_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_11_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_11_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_11_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_11_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_11_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_13.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_13.c deleted file mode 100644 index 3916f2ddeceb..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_13.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-13 character. -static unsigned char yp_encoding_iso_8859_13_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, // Ax - 0, 0, 0, 0, 0, 3, 0, 0, 3, 0, 3, 0, 0, 0, 0, 3, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx -}; - -size_t -yp_encoding_iso_8859_13_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_13_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_13_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_13_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_13_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_13_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_14.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_14.c deleted file mode 100644 index ae5cbeb1d746..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_14.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-14 character. -static unsigned char yp_encoding_iso_8859_14_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 7, 3, 0, 7, 3, 7, 0, 7, 0, 7, 3, 7, 0, 0, 7, // Ax - 7, 3, 7, 3, 7, 3, 0, 7, 3, 3, 3, 7, 3, 7, 3, 3, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx -}; - -size_t -yp_encoding_iso_8859_14_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_14_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_14_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_14_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_14_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_14_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_15.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_15.c deleted file mode 100644 index 73ea8491bc4b..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_15.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-15 character. -static unsigned char yp_encoding_iso_8859_15_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 7, 0, 3, 0, 3, 0, 0, 0, 0, 0, // Ax - 0, 0, 0, 0, 7, 3, 0, 0, 3, 0, 3, 0, 7, 3, 7, 0, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx -}; - -size_t -yp_encoding_iso_8859_15_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_15_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_15_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_15_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_15_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_15_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_16.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_16.c deleted file mode 100644 index d3e6ce666a46..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_16.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-16 character. -static unsigned char yp_encoding_iso_8859_16_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 7, 3, 7, 0, 0, 7, 0, 3, 0, 7, 0, 7, 0, 3, 7, // Ax - 0, 0, 7, 3, 7, 0, 0, 0, 3, 3, 3, 0, 7, 3, 7, 3, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx -}; - -size_t -yp_encoding_iso_8859_16_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_16_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_16_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_16_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_16_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_16_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_2.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_2.c deleted file mode 100644 index bcebc8679bd2..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_2.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-2 character. -static unsigned char yp_encoding_iso_8859_2_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 7, 0, 7, 0, 7, 7, 0, 0, 7, 7, 7, 7, 0, 7, 7, // Ax - 0, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0, 3, 3, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx -}; - -size_t -yp_encoding_iso_8859_2_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_2_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_2_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_2_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_2_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_2_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_3.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_3.c deleted file mode 100644 index 517339e93612..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_3.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-3 character. -static unsigned char yp_encoding_iso_8859_3_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 7, 0, 0, 0, 0, 7, 0, 0, 7, 7, 7, 7, 0, 0, 7, // Ax - 0, 3, 0, 0, 0, 3, 3, 0, 0, 3, 3, 3, 3, 0, 0, 3, // Bx - 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 0, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 0, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx -}; - -size_t -yp_encoding_iso_8859_3_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_3_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_3_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_3_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_3_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_3_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_4.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_4.c deleted file mode 100644 index 0e56974f098b..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_4.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-4 character. -static unsigned char yp_encoding_iso_8859_4_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 7, 3, 7, 0, 7, 7, 0, 0, 7, 7, 7, 7, 0, 7, 0, // Ax - 0, 3, 0, 3, 0, 3, 3, 0, 0, 3, 3, 3, 3, 7, 3, 3, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 0, // Fx -}; - -size_t -yp_encoding_iso_8859_4_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_4_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_4_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_4_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_4_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_4_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_5.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_5.c deleted file mode 100644 index b8dda3e5ea40..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_5.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-5 character. -static unsigned char yp_encoding_iso_8859_5_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, // Ax - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, // Fx -}; - -size_t -yp_encoding_iso_8859_5_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_5_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_5_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_5_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_5_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_5_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_6.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_6.c deleted file mode 100644 index e76f675ae451..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_6.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-6 character. -static unsigned char yp_encoding_iso_8859_6_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Cx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx -}; - -size_t -yp_encoding_iso_8859_6_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_6_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_6_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_6_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_6_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_6_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_7.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_7.c deleted file mode 100644 index 6a3c39dffe1a..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_7.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-7 character. -static unsigned char yp_encoding_iso_8859_7_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax - 0, 0, 0, 0, 0, 0, 7, 0, 7, 7, 7, 0, 7, 0, 7, 7, // Bx - 3, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 3, 3, 3, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, // Fx -}; - -size_t -yp_encoding_iso_8859_7_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_7_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_7_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_7_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_7_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_7_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_8.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_8.c deleted file mode 100644 index 7951d43b7afb..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_8.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-8 character. -static unsigned char yp_encoding_iso_8859_8_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax - 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // Fx -}; - -size_t -yp_encoding_iso_8859_8_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_8_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_8_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_8_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_8_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_8_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/iso_8859_9.c b/src/main/c/yarp/src/yarp/src/enc/iso_8859_9.c deleted file mode 100644 index 7a2a0d510d25..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/iso_8859_9.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding ISO-8859-9 character. -static unsigned char yp_encoding_iso_8859_9_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax - 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx -}; - -size_t -yp_encoding_iso_8859_9_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_9_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_iso_8859_9_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_9_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_iso_8859_9_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_iso_8859_9_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/shift_jis.c b/src/main/c/yarp/src/yarp/src/enc/shift_jis.c deleted file mode 100644 index 4e69ba26c004..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/shift_jis.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -typedef uint32_t shift_jis_codepoint_t; - -static shift_jis_codepoint_t -shift_jis_codepoint(const char *c, size_t *width) { - const unsigned char *uc = (const unsigned char *) c; - - // These are the single byte characters. - if (*uc < 0x80 || (*uc >= 0xA1 && *uc <= 0xDF)) { - *width = 1; - return *uc; - } - - // These are the double byte characters. - if ( - ((uc[0] >= 0x81 && uc[0] <= 0x9F) || (uc[0] >= 0xE0 && uc[0] <= 0xFC)) && - (uc[1] >= 0x40 && uc[1] <= 0xFC) - ) { - *width = 2; - return (shift_jis_codepoint_t) (uc[0] << 8 | uc[1]); - } - - *width = 0; - return 0; -} - -size_t -yp_encoding_shift_jis_char_width(const char *c) { - size_t width; - shift_jis_codepoint(c, &width); - - return width; -} - -size_t -yp_encoding_shift_jis_alpha_char(const char *c) { - size_t width; - shift_jis_codepoint_t codepoint = shift_jis_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_alpha_char(&value); - } else { - return 0; - } -} - -size_t -yp_encoding_shift_jis_alnum_char(const char *c) { - size_t width; - shift_jis_codepoint_t codepoint = shift_jis_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_alnum_char(&value); - } else { - return 0; - } -} - -bool -yp_encoding_shift_jis_isupper_char(const char *c) { - size_t width; - shift_jis_codepoint_t codepoint = shift_jis_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_isupper_char(&value); - } else { - return 0; - } -} diff --git a/src/main/c/yarp/src/yarp/src/enc/unicode.c b/src/main/c/yarp/src/yarp/src/enc/unicode.c deleted file mode 100644 index 818e8eb17c14..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/unicode.c +++ /dev/null @@ -1,2275 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -typedef uint32_t unicode_codepoint_t; - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding unicode codepoint. Note that -// this table is different from other encodings where we used a lookup table -// because the indices of those tables are the byte representations, not the -// codepoints themselves. -static unsigned char yp_encoding_unicode_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax - 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx -}; - -#define UNICODE_ALPHA_CODEPOINTS_LENGTH 1450 -unicode_codepoint_t unicode_alpha_codepoints[UNICODE_ALPHA_CODEPOINTS_LENGTH] = { - 0x100, 0x2C1, - 0x2C6, 0x2D1, - 0x2E0, 0x2E4, - 0x2EC, 0x2EC, - 0x2EE, 0x2EE, - 0x345, 0x345, - 0x370, 0x374, - 0x376, 0x377, - 0x37A, 0x37D, - 0x37F, 0x37F, - 0x386, 0x386, - 0x388, 0x38A, - 0x38C, 0x38C, - 0x38E, 0x3A1, - 0x3A3, 0x3F5, - 0x3F7, 0x481, - 0x48A, 0x52F, - 0x531, 0x556, - 0x559, 0x559, - 0x560, 0x588, - 0x5B0, 0x5BD, - 0x5BF, 0x5BF, - 0x5C1, 0x5C2, - 0x5C4, 0x5C5, - 0x5C7, 0x5C7, - 0x5D0, 0x5EA, - 0x5EF, 0x5F2, - 0x610, 0x61A, - 0x620, 0x657, - 0x659, 0x65F, - 0x66E, 0x6D3, - 0x6D5, 0x6DC, - 0x6E1, 0x6E8, - 0x6ED, 0x6EF, - 0x6FA, 0x6FC, - 0x6FF, 0x6FF, - 0x710, 0x73F, - 0x74D, 0x7B1, - 0x7CA, 0x7EA, - 0x7F4, 0x7F5, - 0x7FA, 0x7FA, - 0x800, 0x817, - 0x81A, 0x82C, - 0x840, 0x858, - 0x860, 0x86A, - 0x870, 0x887, - 0x889, 0x88E, - 0x8A0, 0x8C9, - 0x8D4, 0x8DF, - 0x8E3, 0x8E9, - 0x8F0, 0x93B, - 0x93D, 0x94C, - 0x94E, 0x950, - 0x955, 0x963, - 0x971, 0x983, - 0x985, 0x98C, - 0x98F, 0x990, - 0x993, 0x9A8, - 0x9AA, 0x9B0, - 0x9B2, 0x9B2, - 0x9B6, 0x9B9, - 0x9BD, 0x9C4, - 0x9C7, 0x9C8, - 0x9CB, 0x9CC, - 0x9CE, 0x9CE, - 0x9D7, 0x9D7, - 0x9DC, 0x9DD, - 0x9DF, 0x9E3, - 0x9F0, 0x9F1, - 0x9FC, 0x9FC, - 0xA01, 0xA03, - 0xA05, 0xA0A, - 0xA0F, 0xA10, - 0xA13, 0xA28, - 0xA2A, 0xA30, - 0xA32, 0xA33, - 0xA35, 0xA36, - 0xA38, 0xA39, - 0xA3E, 0xA42, - 0xA47, 0xA48, - 0xA4B, 0xA4C, - 0xA51, 0xA51, - 0xA59, 0xA5C, - 0xA5E, 0xA5E, - 0xA70, 0xA75, - 0xA81, 0xA83, - 0xA85, 0xA8D, - 0xA8F, 0xA91, - 0xA93, 0xAA8, - 0xAAA, 0xAB0, - 0xAB2, 0xAB3, - 0xAB5, 0xAB9, - 0xABD, 0xAC5, - 0xAC7, 0xAC9, - 0xACB, 0xACC, - 0xAD0, 0xAD0, - 0xAE0, 0xAE3, - 0xAF9, 0xAFC, - 0xB01, 0xB03, - 0xB05, 0xB0C, - 0xB0F, 0xB10, - 0xB13, 0xB28, - 0xB2A, 0xB30, - 0xB32, 0xB33, - 0xB35, 0xB39, - 0xB3D, 0xB44, - 0xB47, 0xB48, - 0xB4B, 0xB4C, - 0xB56, 0xB57, - 0xB5C, 0xB5D, - 0xB5F, 0xB63, - 0xB71, 0xB71, - 0xB82, 0xB83, - 0xB85, 0xB8A, - 0xB8E, 0xB90, - 0xB92, 0xB95, - 0xB99, 0xB9A, - 0xB9C, 0xB9C, - 0xB9E, 0xB9F, - 0xBA3, 0xBA4, - 0xBA8, 0xBAA, - 0xBAE, 0xBB9, - 0xBBE, 0xBC2, - 0xBC6, 0xBC8, - 0xBCA, 0xBCC, - 0xBD0, 0xBD0, - 0xBD7, 0xBD7, - 0xC00, 0xC0C, - 0xC0E, 0xC10, - 0xC12, 0xC28, - 0xC2A, 0xC39, - 0xC3D, 0xC44, - 0xC46, 0xC48, - 0xC4A, 0xC4C, - 0xC55, 0xC56, - 0xC58, 0xC5A, - 0xC5D, 0xC5D, - 0xC60, 0xC63, - 0xC80, 0xC83, - 0xC85, 0xC8C, - 0xC8E, 0xC90, - 0xC92, 0xCA8, - 0xCAA, 0xCB3, - 0xCB5, 0xCB9, - 0xCBD, 0xCC4, - 0xCC6, 0xCC8, - 0xCCA, 0xCCC, - 0xCD5, 0xCD6, - 0xCDD, 0xCDE, - 0xCE0, 0xCE3, - 0xCF1, 0xCF3, - 0xD00, 0xD0C, - 0xD0E, 0xD10, - 0xD12, 0xD3A, - 0xD3D, 0xD44, - 0xD46, 0xD48, - 0xD4A, 0xD4C, - 0xD4E, 0xD4E, - 0xD54, 0xD57, - 0xD5F, 0xD63, - 0xD7A, 0xD7F, - 0xD81, 0xD83, - 0xD85, 0xD96, - 0xD9A, 0xDB1, - 0xDB3, 0xDBB, - 0xDBD, 0xDBD, - 0xDC0, 0xDC6, - 0xDCF, 0xDD4, - 0xDD6, 0xDD6, - 0xDD8, 0xDDF, - 0xDF2, 0xDF3, - 0xE01, 0xE3A, - 0xE40, 0xE46, - 0xE4D, 0xE4D, - 0xE81, 0xE82, - 0xE84, 0xE84, - 0xE86, 0xE8A, - 0xE8C, 0xEA3, - 0xEA5, 0xEA5, - 0xEA7, 0xEB9, - 0xEBB, 0xEBD, - 0xEC0, 0xEC4, - 0xEC6, 0xEC6, - 0xECD, 0xECD, - 0xEDC, 0xEDF, - 0xF00, 0xF00, - 0xF40, 0xF47, - 0xF49, 0xF6C, - 0xF71, 0xF83, - 0xF88, 0xF97, - 0xF99, 0xFBC, - 0x1000, 0x1036, - 0x1038, 0x1038, - 0x103B, 0x103F, - 0x1050, 0x108F, - 0x109A, 0x109D, - 0x10A0, 0x10C5, - 0x10C7, 0x10C7, - 0x10CD, 0x10CD, - 0x10D0, 0x10FA, - 0x10FC, 0x1248, - 0x124A, 0x124D, - 0x1250, 0x1256, - 0x1258, 0x1258, - 0x125A, 0x125D, - 0x1260, 0x1288, - 0x128A, 0x128D, - 0x1290, 0x12B0, - 0x12B2, 0x12B5, - 0x12B8, 0x12BE, - 0x12C0, 0x12C0, - 0x12C2, 0x12C5, - 0x12C8, 0x12D6, - 0x12D8, 0x1310, - 0x1312, 0x1315, - 0x1318, 0x135A, - 0x1380, 0x138F, - 0x13A0, 0x13F5, - 0x13F8, 0x13FD, - 0x1401, 0x166C, - 0x166F, 0x167F, - 0x1681, 0x169A, - 0x16A0, 0x16EA, - 0x16EE, 0x16F8, - 0x1700, 0x1713, - 0x171F, 0x1733, - 0x1740, 0x1753, - 0x1760, 0x176C, - 0x176E, 0x1770, - 0x1772, 0x1773, - 0x1780, 0x17B3, - 0x17B6, 0x17C8, - 0x17D7, 0x17D7, - 0x17DC, 0x17DC, - 0x1820, 0x1878, - 0x1880, 0x18AA, - 0x18B0, 0x18F5, - 0x1900, 0x191E, - 0x1920, 0x192B, - 0x1930, 0x1938, - 0x1950, 0x196D, - 0x1970, 0x1974, - 0x1980, 0x19AB, - 0x19B0, 0x19C9, - 0x1A00, 0x1A1B, - 0x1A20, 0x1A5E, - 0x1A61, 0x1A74, - 0x1AA7, 0x1AA7, - 0x1ABF, 0x1AC0, - 0x1ACC, 0x1ACE, - 0x1B00, 0x1B33, - 0x1B35, 0x1B43, - 0x1B45, 0x1B4C, - 0x1B80, 0x1BA9, - 0x1BAC, 0x1BAF, - 0x1BBA, 0x1BE5, - 0x1BE7, 0x1BF1, - 0x1C00, 0x1C36, - 0x1C4D, 0x1C4F, - 0x1C5A, 0x1C7D, - 0x1C80, 0x1C88, - 0x1C90, 0x1CBA, - 0x1CBD, 0x1CBF, - 0x1CE9, 0x1CEC, - 0x1CEE, 0x1CF3, - 0x1CF5, 0x1CF6, - 0x1CFA, 0x1CFA, - 0x1D00, 0x1DBF, - 0x1DE7, 0x1DF4, - 0x1E00, 0x1F15, - 0x1F18, 0x1F1D, - 0x1F20, 0x1F45, - 0x1F48, 0x1F4D, - 0x1F50, 0x1F57, - 0x1F59, 0x1F59, - 0x1F5B, 0x1F5B, - 0x1F5D, 0x1F5D, - 0x1F5F, 0x1F7D, - 0x1F80, 0x1FB4, - 0x1FB6, 0x1FBC, - 0x1FBE, 0x1FBE, - 0x1FC2, 0x1FC4, - 0x1FC6, 0x1FCC, - 0x1FD0, 0x1FD3, - 0x1FD6, 0x1FDB, - 0x1FE0, 0x1FEC, - 0x1FF2, 0x1FF4, - 0x1FF6, 0x1FFC, - 0x2071, 0x2071, - 0x207F, 0x207F, - 0x2090, 0x209C, - 0x2102, 0x2102, - 0x2107, 0x2107, - 0x210A, 0x2113, - 0x2115, 0x2115, - 0x2119, 0x211D, - 0x2124, 0x2124, - 0x2126, 0x2126, - 0x2128, 0x2128, - 0x212A, 0x212D, - 0x212F, 0x2139, - 0x213C, 0x213F, - 0x2145, 0x2149, - 0x214E, 0x214E, - 0x2160, 0x2188, - 0x24B6, 0x24E9, - 0x2C00, 0x2CE4, - 0x2CEB, 0x2CEE, - 0x2CF2, 0x2CF3, - 0x2D00, 0x2D25, - 0x2D27, 0x2D27, - 0x2D2D, 0x2D2D, - 0x2D30, 0x2D67, - 0x2D6F, 0x2D6F, - 0x2D80, 0x2D96, - 0x2DA0, 0x2DA6, - 0x2DA8, 0x2DAE, - 0x2DB0, 0x2DB6, - 0x2DB8, 0x2DBE, - 0x2DC0, 0x2DC6, - 0x2DC8, 0x2DCE, - 0x2DD0, 0x2DD6, - 0x2DD8, 0x2DDE, - 0x2DE0, 0x2DFF, - 0x2E2F, 0x2E2F, - 0x3005, 0x3007, - 0x3021, 0x3029, - 0x3031, 0x3035, - 0x3038, 0x303C, - 0x3041, 0x3096, - 0x309D, 0x309F, - 0x30A1, 0x30FA, - 0x30FC, 0x30FF, - 0x3105, 0x312F, - 0x3131, 0x318E, - 0x31A0, 0x31BF, - 0x31F0, 0x31FF, - 0x3400, 0x4DBF, - 0x4E00, 0xA48C, - 0xA4D0, 0xA4FD, - 0xA500, 0xA60C, - 0xA610, 0xA61F, - 0xA62A, 0xA62B, - 0xA640, 0xA66E, - 0xA674, 0xA67B, - 0xA67F, 0xA6EF, - 0xA717, 0xA71F, - 0xA722, 0xA788, - 0xA78B, 0xA7CA, - 0xA7D0, 0xA7D1, - 0xA7D3, 0xA7D3, - 0xA7D5, 0xA7D9, - 0xA7F2, 0xA805, - 0xA807, 0xA827, - 0xA840, 0xA873, - 0xA880, 0xA8C3, - 0xA8C5, 0xA8C5, - 0xA8F2, 0xA8F7, - 0xA8FB, 0xA8FB, - 0xA8FD, 0xA8FF, - 0xA90A, 0xA92A, - 0xA930, 0xA952, - 0xA960, 0xA97C, - 0xA980, 0xA9B2, - 0xA9B4, 0xA9BF, - 0xA9CF, 0xA9CF, - 0xA9E0, 0xA9EF, - 0xA9FA, 0xA9FE, - 0xAA00, 0xAA36, - 0xAA40, 0xAA4D, - 0xAA60, 0xAA76, - 0xAA7A, 0xAABE, - 0xAAC0, 0xAAC0, - 0xAAC2, 0xAAC2, - 0xAADB, 0xAADD, - 0xAAE0, 0xAAEF, - 0xAAF2, 0xAAF5, - 0xAB01, 0xAB06, - 0xAB09, 0xAB0E, - 0xAB11, 0xAB16, - 0xAB20, 0xAB26, - 0xAB28, 0xAB2E, - 0xAB30, 0xAB5A, - 0xAB5C, 0xAB69, - 0xAB70, 0xABEA, - 0xAC00, 0xD7A3, - 0xD7B0, 0xD7C6, - 0xD7CB, 0xD7FB, - 0xF900, 0xFA6D, - 0xFA70, 0xFAD9, - 0xFB00, 0xFB06, - 0xFB13, 0xFB17, - 0xFB1D, 0xFB28, - 0xFB2A, 0xFB36, - 0xFB38, 0xFB3C, - 0xFB3E, 0xFB3E, - 0xFB40, 0xFB41, - 0xFB43, 0xFB44, - 0xFB46, 0xFBB1, - 0xFBD3, 0xFD3D, - 0xFD50, 0xFD8F, - 0xFD92, 0xFDC7, - 0xFDF0, 0xFDFB, - 0xFE70, 0xFE74, - 0xFE76, 0xFEFC, - 0xFF21, 0xFF3A, - 0xFF41, 0xFF5A, - 0xFF66, 0xFFBE, - 0xFFC2, 0xFFC7, - 0xFFCA, 0xFFCF, - 0xFFD2, 0xFFD7, - 0xFFDA, 0xFFDC, - 0x10000, 0x1000B, - 0x1000D, 0x10026, - 0x10028, 0x1003A, - 0x1003C, 0x1003D, - 0x1003F, 0x1004D, - 0x10050, 0x1005D, - 0x10080, 0x100FA, - 0x10140, 0x10174, - 0x10280, 0x1029C, - 0x102A0, 0x102D0, - 0x10300, 0x1031F, - 0x1032D, 0x1034A, - 0x10350, 0x1037A, - 0x10380, 0x1039D, - 0x103A0, 0x103C3, - 0x103C8, 0x103CF, - 0x103D1, 0x103D5, - 0x10400, 0x1049D, - 0x104B0, 0x104D3, - 0x104D8, 0x104FB, - 0x10500, 0x10527, - 0x10530, 0x10563, - 0x10570, 0x1057A, - 0x1057C, 0x1058A, - 0x1058C, 0x10592, - 0x10594, 0x10595, - 0x10597, 0x105A1, - 0x105A3, 0x105B1, - 0x105B3, 0x105B9, - 0x105BB, 0x105BC, - 0x10600, 0x10736, - 0x10740, 0x10755, - 0x10760, 0x10767, - 0x10780, 0x10785, - 0x10787, 0x107B0, - 0x107B2, 0x107BA, - 0x10800, 0x10805, - 0x10808, 0x10808, - 0x1080A, 0x10835, - 0x10837, 0x10838, - 0x1083C, 0x1083C, - 0x1083F, 0x10855, - 0x10860, 0x10876, - 0x10880, 0x1089E, - 0x108E0, 0x108F2, - 0x108F4, 0x108F5, - 0x10900, 0x10915, - 0x10920, 0x10939, - 0x10980, 0x109B7, - 0x109BE, 0x109BF, - 0x10A00, 0x10A03, - 0x10A05, 0x10A06, - 0x10A0C, 0x10A13, - 0x10A15, 0x10A17, - 0x10A19, 0x10A35, - 0x10A60, 0x10A7C, - 0x10A80, 0x10A9C, - 0x10AC0, 0x10AC7, - 0x10AC9, 0x10AE4, - 0x10B00, 0x10B35, - 0x10B40, 0x10B55, - 0x10B60, 0x10B72, - 0x10B80, 0x10B91, - 0x10C00, 0x10C48, - 0x10C80, 0x10CB2, - 0x10CC0, 0x10CF2, - 0x10D00, 0x10D27, - 0x10E80, 0x10EA9, - 0x10EAB, 0x10EAC, - 0x10EB0, 0x10EB1, - 0x10F00, 0x10F1C, - 0x10F27, 0x10F27, - 0x10F30, 0x10F45, - 0x10F70, 0x10F81, - 0x10FB0, 0x10FC4, - 0x10FE0, 0x10FF6, - 0x11000, 0x11045, - 0x11071, 0x11075, - 0x11080, 0x110B8, - 0x110C2, 0x110C2, - 0x110D0, 0x110E8, - 0x11100, 0x11132, - 0x11144, 0x11147, - 0x11150, 0x11172, - 0x11176, 0x11176, - 0x11180, 0x111BF, - 0x111C1, 0x111C4, - 0x111CE, 0x111CF, - 0x111DA, 0x111DA, - 0x111DC, 0x111DC, - 0x11200, 0x11211, - 0x11213, 0x11234, - 0x11237, 0x11237, - 0x1123E, 0x11241, - 0x11280, 0x11286, - 0x11288, 0x11288, - 0x1128A, 0x1128D, - 0x1128F, 0x1129D, - 0x1129F, 0x112A8, - 0x112B0, 0x112E8, - 0x11300, 0x11303, - 0x11305, 0x1130C, - 0x1130F, 0x11310, - 0x11313, 0x11328, - 0x1132A, 0x11330, - 0x11332, 0x11333, - 0x11335, 0x11339, - 0x1133D, 0x11344, - 0x11347, 0x11348, - 0x1134B, 0x1134C, - 0x11350, 0x11350, - 0x11357, 0x11357, - 0x1135D, 0x11363, - 0x11400, 0x11441, - 0x11443, 0x11445, - 0x11447, 0x1144A, - 0x1145F, 0x11461, - 0x11480, 0x114C1, - 0x114C4, 0x114C5, - 0x114C7, 0x114C7, - 0x11580, 0x115B5, - 0x115B8, 0x115BE, - 0x115D8, 0x115DD, - 0x11600, 0x1163E, - 0x11640, 0x11640, - 0x11644, 0x11644, - 0x11680, 0x116B5, - 0x116B8, 0x116B8, - 0x11700, 0x1171A, - 0x1171D, 0x1172A, - 0x11740, 0x11746, - 0x11800, 0x11838, - 0x118A0, 0x118DF, - 0x118FF, 0x11906, - 0x11909, 0x11909, - 0x1190C, 0x11913, - 0x11915, 0x11916, - 0x11918, 0x11935, - 0x11937, 0x11938, - 0x1193B, 0x1193C, - 0x1193F, 0x11942, - 0x119A0, 0x119A7, - 0x119AA, 0x119D7, - 0x119DA, 0x119DF, - 0x119E1, 0x119E1, - 0x119E3, 0x119E4, - 0x11A00, 0x11A32, - 0x11A35, 0x11A3E, - 0x11A50, 0x11A97, - 0x11A9D, 0x11A9D, - 0x11AB0, 0x11AF8, - 0x11C00, 0x11C08, - 0x11C0A, 0x11C36, - 0x11C38, 0x11C3E, - 0x11C40, 0x11C40, - 0x11C72, 0x11C8F, - 0x11C92, 0x11CA7, - 0x11CA9, 0x11CB6, - 0x11D00, 0x11D06, - 0x11D08, 0x11D09, - 0x11D0B, 0x11D36, - 0x11D3A, 0x11D3A, - 0x11D3C, 0x11D3D, - 0x11D3F, 0x11D41, - 0x11D43, 0x11D43, - 0x11D46, 0x11D47, - 0x11D60, 0x11D65, - 0x11D67, 0x11D68, - 0x11D6A, 0x11D8E, - 0x11D90, 0x11D91, - 0x11D93, 0x11D96, - 0x11D98, 0x11D98, - 0x11EE0, 0x11EF6, - 0x11F00, 0x11F10, - 0x11F12, 0x11F3A, - 0x11F3E, 0x11F40, - 0x11FB0, 0x11FB0, - 0x12000, 0x12399, - 0x12400, 0x1246E, - 0x12480, 0x12543, - 0x12F90, 0x12FF0, - 0x13000, 0x1342F, - 0x13441, 0x13446, - 0x14400, 0x14646, - 0x16800, 0x16A38, - 0x16A40, 0x16A5E, - 0x16A70, 0x16ABE, - 0x16AD0, 0x16AED, - 0x16B00, 0x16B2F, - 0x16B40, 0x16B43, - 0x16B63, 0x16B77, - 0x16B7D, 0x16B8F, - 0x16E40, 0x16E7F, - 0x16F00, 0x16F4A, - 0x16F4F, 0x16F87, - 0x16F8F, 0x16F9F, - 0x16FE0, 0x16FE1, - 0x16FE3, 0x16FE3, - 0x16FF0, 0x16FF1, - 0x17000, 0x187F7, - 0x18800, 0x18CD5, - 0x18D00, 0x18D08, - 0x1AFF0, 0x1AFF3, - 0x1AFF5, 0x1AFFB, - 0x1AFFD, 0x1AFFE, - 0x1B000, 0x1B122, - 0x1B132, 0x1B132, - 0x1B150, 0x1B152, - 0x1B155, 0x1B155, - 0x1B164, 0x1B167, - 0x1B170, 0x1B2FB, - 0x1BC00, 0x1BC6A, - 0x1BC70, 0x1BC7C, - 0x1BC80, 0x1BC88, - 0x1BC90, 0x1BC99, - 0x1BC9E, 0x1BC9E, - 0x1D400, 0x1D454, - 0x1D456, 0x1D49C, - 0x1D49E, 0x1D49F, - 0x1D4A2, 0x1D4A2, - 0x1D4A5, 0x1D4A6, - 0x1D4A9, 0x1D4AC, - 0x1D4AE, 0x1D4B9, - 0x1D4BB, 0x1D4BB, - 0x1D4BD, 0x1D4C3, - 0x1D4C5, 0x1D505, - 0x1D507, 0x1D50A, - 0x1D50D, 0x1D514, - 0x1D516, 0x1D51C, - 0x1D51E, 0x1D539, - 0x1D53B, 0x1D53E, - 0x1D540, 0x1D544, - 0x1D546, 0x1D546, - 0x1D54A, 0x1D550, - 0x1D552, 0x1D6A5, - 0x1D6A8, 0x1D6C0, - 0x1D6C2, 0x1D6DA, - 0x1D6DC, 0x1D6FA, - 0x1D6FC, 0x1D714, - 0x1D716, 0x1D734, - 0x1D736, 0x1D74E, - 0x1D750, 0x1D76E, - 0x1D770, 0x1D788, - 0x1D78A, 0x1D7A8, - 0x1D7AA, 0x1D7C2, - 0x1D7C4, 0x1D7CB, - 0x1DF00, 0x1DF1E, - 0x1DF25, 0x1DF2A, - 0x1E000, 0x1E006, - 0x1E008, 0x1E018, - 0x1E01B, 0x1E021, - 0x1E023, 0x1E024, - 0x1E026, 0x1E02A, - 0x1E030, 0x1E06D, - 0x1E08F, 0x1E08F, - 0x1E100, 0x1E12C, - 0x1E137, 0x1E13D, - 0x1E14E, 0x1E14E, - 0x1E290, 0x1E2AD, - 0x1E2C0, 0x1E2EB, - 0x1E4D0, 0x1E4EB, - 0x1E7E0, 0x1E7E6, - 0x1E7E8, 0x1E7EB, - 0x1E7ED, 0x1E7EE, - 0x1E7F0, 0x1E7FE, - 0x1E800, 0x1E8C4, - 0x1E900, 0x1E943, - 0x1E947, 0x1E947, - 0x1E94B, 0x1E94B, - 0x1EE00, 0x1EE03, - 0x1EE05, 0x1EE1F, - 0x1EE21, 0x1EE22, - 0x1EE24, 0x1EE24, - 0x1EE27, 0x1EE27, - 0x1EE29, 0x1EE32, - 0x1EE34, 0x1EE37, - 0x1EE39, 0x1EE39, - 0x1EE3B, 0x1EE3B, - 0x1EE42, 0x1EE42, - 0x1EE47, 0x1EE47, - 0x1EE49, 0x1EE49, - 0x1EE4B, 0x1EE4B, - 0x1EE4D, 0x1EE4F, - 0x1EE51, 0x1EE52, - 0x1EE54, 0x1EE54, - 0x1EE57, 0x1EE57, - 0x1EE59, 0x1EE59, - 0x1EE5B, 0x1EE5B, - 0x1EE5D, 0x1EE5D, - 0x1EE5F, 0x1EE5F, - 0x1EE61, 0x1EE62, - 0x1EE64, 0x1EE64, - 0x1EE67, 0x1EE6A, - 0x1EE6C, 0x1EE72, - 0x1EE74, 0x1EE77, - 0x1EE79, 0x1EE7C, - 0x1EE7E, 0x1EE7E, - 0x1EE80, 0x1EE89, - 0x1EE8B, 0x1EE9B, - 0x1EEA1, 0x1EEA3, - 0x1EEA5, 0x1EEA9, - 0x1EEAB, 0x1EEBB, - 0x1F130, 0x1F149, - 0x1F150, 0x1F169, - 0x1F170, 0x1F189, - 0x20000, 0x2A6DF, - 0x2A700, 0x2B739, - 0x2B740, 0x2B81D, - 0x2B820, 0x2CEA1, - 0x2CEB0, 0x2EBE0, - 0x2F800, 0x2FA1D, - 0x30000, 0x3134A, - 0x31350, 0x323AF, -}; - -#define UNICODE_ALNUM_CODEPOINTS_LENGTH 1528 -unicode_codepoint_t unicode_alnum_codepoints[UNICODE_ALNUM_CODEPOINTS_LENGTH] = { - 0x100, 0x2C1, - 0x2C6, 0x2D1, - 0x2E0, 0x2E4, - 0x2EC, 0x2EC, - 0x2EE, 0x2EE, - 0x345, 0x345, - 0x370, 0x374, - 0x376, 0x377, - 0x37A, 0x37D, - 0x37F, 0x37F, - 0x386, 0x386, - 0x388, 0x38A, - 0x38C, 0x38C, - 0x38E, 0x3A1, - 0x3A3, 0x3F5, - 0x3F7, 0x481, - 0x48A, 0x52F, - 0x531, 0x556, - 0x559, 0x559, - 0x560, 0x588, - 0x5B0, 0x5BD, - 0x5BF, 0x5BF, - 0x5C1, 0x5C2, - 0x5C4, 0x5C5, - 0x5C7, 0x5C7, - 0x5D0, 0x5EA, - 0x5EF, 0x5F2, - 0x610, 0x61A, - 0x620, 0x657, - 0x659, 0x669, - 0x66E, 0x6D3, - 0x6D5, 0x6DC, - 0x6E1, 0x6E8, - 0x6ED, 0x6FC, - 0x6FF, 0x6FF, - 0x710, 0x73F, - 0x74D, 0x7B1, - 0x7C0, 0x7EA, - 0x7F4, 0x7F5, - 0x7FA, 0x7FA, - 0x800, 0x817, - 0x81A, 0x82C, - 0x840, 0x858, - 0x860, 0x86A, - 0x870, 0x887, - 0x889, 0x88E, - 0x8A0, 0x8C9, - 0x8D4, 0x8DF, - 0x8E3, 0x8E9, - 0x8F0, 0x93B, - 0x93D, 0x94C, - 0x94E, 0x950, - 0x955, 0x963, - 0x966, 0x96F, - 0x971, 0x983, - 0x985, 0x98C, - 0x98F, 0x990, - 0x993, 0x9A8, - 0x9AA, 0x9B0, - 0x9B2, 0x9B2, - 0x9B6, 0x9B9, - 0x9BD, 0x9C4, - 0x9C7, 0x9C8, - 0x9CB, 0x9CC, - 0x9CE, 0x9CE, - 0x9D7, 0x9D7, - 0x9DC, 0x9DD, - 0x9DF, 0x9E3, - 0x9E6, 0x9F1, - 0x9FC, 0x9FC, - 0xA01, 0xA03, - 0xA05, 0xA0A, - 0xA0F, 0xA10, - 0xA13, 0xA28, - 0xA2A, 0xA30, - 0xA32, 0xA33, - 0xA35, 0xA36, - 0xA38, 0xA39, - 0xA3E, 0xA42, - 0xA47, 0xA48, - 0xA4B, 0xA4C, - 0xA51, 0xA51, - 0xA59, 0xA5C, - 0xA5E, 0xA5E, - 0xA66, 0xA75, - 0xA81, 0xA83, - 0xA85, 0xA8D, - 0xA8F, 0xA91, - 0xA93, 0xAA8, - 0xAAA, 0xAB0, - 0xAB2, 0xAB3, - 0xAB5, 0xAB9, - 0xABD, 0xAC5, - 0xAC7, 0xAC9, - 0xACB, 0xACC, - 0xAD0, 0xAD0, - 0xAE0, 0xAE3, - 0xAE6, 0xAEF, - 0xAF9, 0xAFC, - 0xB01, 0xB03, - 0xB05, 0xB0C, - 0xB0F, 0xB10, - 0xB13, 0xB28, - 0xB2A, 0xB30, - 0xB32, 0xB33, - 0xB35, 0xB39, - 0xB3D, 0xB44, - 0xB47, 0xB48, - 0xB4B, 0xB4C, - 0xB56, 0xB57, - 0xB5C, 0xB5D, - 0xB5F, 0xB63, - 0xB66, 0xB6F, - 0xB71, 0xB71, - 0xB82, 0xB83, - 0xB85, 0xB8A, - 0xB8E, 0xB90, - 0xB92, 0xB95, - 0xB99, 0xB9A, - 0xB9C, 0xB9C, - 0xB9E, 0xB9F, - 0xBA3, 0xBA4, - 0xBA8, 0xBAA, - 0xBAE, 0xBB9, - 0xBBE, 0xBC2, - 0xBC6, 0xBC8, - 0xBCA, 0xBCC, - 0xBD0, 0xBD0, - 0xBD7, 0xBD7, - 0xBE6, 0xBEF, - 0xC00, 0xC0C, - 0xC0E, 0xC10, - 0xC12, 0xC28, - 0xC2A, 0xC39, - 0xC3D, 0xC44, - 0xC46, 0xC48, - 0xC4A, 0xC4C, - 0xC55, 0xC56, - 0xC58, 0xC5A, - 0xC5D, 0xC5D, - 0xC60, 0xC63, - 0xC66, 0xC6F, - 0xC80, 0xC83, - 0xC85, 0xC8C, - 0xC8E, 0xC90, - 0xC92, 0xCA8, - 0xCAA, 0xCB3, - 0xCB5, 0xCB9, - 0xCBD, 0xCC4, - 0xCC6, 0xCC8, - 0xCCA, 0xCCC, - 0xCD5, 0xCD6, - 0xCDD, 0xCDE, - 0xCE0, 0xCE3, - 0xCE6, 0xCEF, - 0xCF1, 0xCF3, - 0xD00, 0xD0C, - 0xD0E, 0xD10, - 0xD12, 0xD3A, - 0xD3D, 0xD44, - 0xD46, 0xD48, - 0xD4A, 0xD4C, - 0xD4E, 0xD4E, - 0xD54, 0xD57, - 0xD5F, 0xD63, - 0xD66, 0xD6F, - 0xD7A, 0xD7F, - 0xD81, 0xD83, - 0xD85, 0xD96, - 0xD9A, 0xDB1, - 0xDB3, 0xDBB, - 0xDBD, 0xDBD, - 0xDC0, 0xDC6, - 0xDCF, 0xDD4, - 0xDD6, 0xDD6, - 0xDD8, 0xDDF, - 0xDE6, 0xDEF, - 0xDF2, 0xDF3, - 0xE01, 0xE3A, - 0xE40, 0xE46, - 0xE4D, 0xE4D, - 0xE50, 0xE59, - 0xE81, 0xE82, - 0xE84, 0xE84, - 0xE86, 0xE8A, - 0xE8C, 0xEA3, - 0xEA5, 0xEA5, - 0xEA7, 0xEB9, - 0xEBB, 0xEBD, - 0xEC0, 0xEC4, - 0xEC6, 0xEC6, - 0xECD, 0xECD, - 0xED0, 0xED9, - 0xEDC, 0xEDF, - 0xF00, 0xF00, - 0xF20, 0xF29, - 0xF40, 0xF47, - 0xF49, 0xF6C, - 0xF71, 0xF83, - 0xF88, 0xF97, - 0xF99, 0xFBC, - 0x1000, 0x1036, - 0x1038, 0x1038, - 0x103B, 0x1049, - 0x1050, 0x109D, - 0x10A0, 0x10C5, - 0x10C7, 0x10C7, - 0x10CD, 0x10CD, - 0x10D0, 0x10FA, - 0x10FC, 0x1248, - 0x124A, 0x124D, - 0x1250, 0x1256, - 0x1258, 0x1258, - 0x125A, 0x125D, - 0x1260, 0x1288, - 0x128A, 0x128D, - 0x1290, 0x12B0, - 0x12B2, 0x12B5, - 0x12B8, 0x12BE, - 0x12C0, 0x12C0, - 0x12C2, 0x12C5, - 0x12C8, 0x12D6, - 0x12D8, 0x1310, - 0x1312, 0x1315, - 0x1318, 0x135A, - 0x1380, 0x138F, - 0x13A0, 0x13F5, - 0x13F8, 0x13FD, - 0x1401, 0x166C, - 0x166F, 0x167F, - 0x1681, 0x169A, - 0x16A0, 0x16EA, - 0x16EE, 0x16F8, - 0x1700, 0x1713, - 0x171F, 0x1733, - 0x1740, 0x1753, - 0x1760, 0x176C, - 0x176E, 0x1770, - 0x1772, 0x1773, - 0x1780, 0x17B3, - 0x17B6, 0x17C8, - 0x17D7, 0x17D7, - 0x17DC, 0x17DC, - 0x17E0, 0x17E9, - 0x1810, 0x1819, - 0x1820, 0x1878, - 0x1880, 0x18AA, - 0x18B0, 0x18F5, - 0x1900, 0x191E, - 0x1920, 0x192B, - 0x1930, 0x1938, - 0x1946, 0x196D, - 0x1970, 0x1974, - 0x1980, 0x19AB, - 0x19B0, 0x19C9, - 0x19D0, 0x19D9, - 0x1A00, 0x1A1B, - 0x1A20, 0x1A5E, - 0x1A61, 0x1A74, - 0x1A80, 0x1A89, - 0x1A90, 0x1A99, - 0x1AA7, 0x1AA7, - 0x1ABF, 0x1AC0, - 0x1ACC, 0x1ACE, - 0x1B00, 0x1B33, - 0x1B35, 0x1B43, - 0x1B45, 0x1B4C, - 0x1B50, 0x1B59, - 0x1B80, 0x1BA9, - 0x1BAC, 0x1BE5, - 0x1BE7, 0x1BF1, - 0x1C00, 0x1C36, - 0x1C40, 0x1C49, - 0x1C4D, 0x1C7D, - 0x1C80, 0x1C88, - 0x1C90, 0x1CBA, - 0x1CBD, 0x1CBF, - 0x1CE9, 0x1CEC, - 0x1CEE, 0x1CF3, - 0x1CF5, 0x1CF6, - 0x1CFA, 0x1CFA, - 0x1D00, 0x1DBF, - 0x1DE7, 0x1DF4, - 0x1E00, 0x1F15, - 0x1F18, 0x1F1D, - 0x1F20, 0x1F45, - 0x1F48, 0x1F4D, - 0x1F50, 0x1F57, - 0x1F59, 0x1F59, - 0x1F5B, 0x1F5B, - 0x1F5D, 0x1F5D, - 0x1F5F, 0x1F7D, - 0x1F80, 0x1FB4, - 0x1FB6, 0x1FBC, - 0x1FBE, 0x1FBE, - 0x1FC2, 0x1FC4, - 0x1FC6, 0x1FCC, - 0x1FD0, 0x1FD3, - 0x1FD6, 0x1FDB, - 0x1FE0, 0x1FEC, - 0x1FF2, 0x1FF4, - 0x1FF6, 0x1FFC, - 0x2071, 0x2071, - 0x207F, 0x207F, - 0x2090, 0x209C, - 0x2102, 0x2102, - 0x2107, 0x2107, - 0x210A, 0x2113, - 0x2115, 0x2115, - 0x2119, 0x211D, - 0x2124, 0x2124, - 0x2126, 0x2126, - 0x2128, 0x2128, - 0x212A, 0x212D, - 0x212F, 0x2139, - 0x213C, 0x213F, - 0x2145, 0x2149, - 0x214E, 0x214E, - 0x2160, 0x2188, - 0x24B6, 0x24E9, - 0x2C00, 0x2CE4, - 0x2CEB, 0x2CEE, - 0x2CF2, 0x2CF3, - 0x2D00, 0x2D25, - 0x2D27, 0x2D27, - 0x2D2D, 0x2D2D, - 0x2D30, 0x2D67, - 0x2D6F, 0x2D6F, - 0x2D80, 0x2D96, - 0x2DA0, 0x2DA6, - 0x2DA8, 0x2DAE, - 0x2DB0, 0x2DB6, - 0x2DB8, 0x2DBE, - 0x2DC0, 0x2DC6, - 0x2DC8, 0x2DCE, - 0x2DD0, 0x2DD6, - 0x2DD8, 0x2DDE, - 0x2DE0, 0x2DFF, - 0x2E2F, 0x2E2F, - 0x3005, 0x3007, - 0x3021, 0x3029, - 0x3031, 0x3035, - 0x3038, 0x303C, - 0x3041, 0x3096, - 0x309D, 0x309F, - 0x30A1, 0x30FA, - 0x30FC, 0x30FF, - 0x3105, 0x312F, - 0x3131, 0x318E, - 0x31A0, 0x31BF, - 0x31F0, 0x31FF, - 0x3400, 0x4DBF, - 0x4E00, 0xA48C, - 0xA4D0, 0xA4FD, - 0xA500, 0xA60C, - 0xA610, 0xA62B, - 0xA640, 0xA66E, - 0xA674, 0xA67B, - 0xA67F, 0xA6EF, - 0xA717, 0xA71F, - 0xA722, 0xA788, - 0xA78B, 0xA7CA, - 0xA7D0, 0xA7D1, - 0xA7D3, 0xA7D3, - 0xA7D5, 0xA7D9, - 0xA7F2, 0xA805, - 0xA807, 0xA827, - 0xA840, 0xA873, - 0xA880, 0xA8C3, - 0xA8C5, 0xA8C5, - 0xA8D0, 0xA8D9, - 0xA8F2, 0xA8F7, - 0xA8FB, 0xA8FB, - 0xA8FD, 0xA92A, - 0xA930, 0xA952, - 0xA960, 0xA97C, - 0xA980, 0xA9B2, - 0xA9B4, 0xA9BF, - 0xA9CF, 0xA9D9, - 0xA9E0, 0xA9FE, - 0xAA00, 0xAA36, - 0xAA40, 0xAA4D, - 0xAA50, 0xAA59, - 0xAA60, 0xAA76, - 0xAA7A, 0xAABE, - 0xAAC0, 0xAAC0, - 0xAAC2, 0xAAC2, - 0xAADB, 0xAADD, - 0xAAE0, 0xAAEF, - 0xAAF2, 0xAAF5, - 0xAB01, 0xAB06, - 0xAB09, 0xAB0E, - 0xAB11, 0xAB16, - 0xAB20, 0xAB26, - 0xAB28, 0xAB2E, - 0xAB30, 0xAB5A, - 0xAB5C, 0xAB69, - 0xAB70, 0xABEA, - 0xABF0, 0xABF9, - 0xAC00, 0xD7A3, - 0xD7B0, 0xD7C6, - 0xD7CB, 0xD7FB, - 0xF900, 0xFA6D, - 0xFA70, 0xFAD9, - 0xFB00, 0xFB06, - 0xFB13, 0xFB17, - 0xFB1D, 0xFB28, - 0xFB2A, 0xFB36, - 0xFB38, 0xFB3C, - 0xFB3E, 0xFB3E, - 0xFB40, 0xFB41, - 0xFB43, 0xFB44, - 0xFB46, 0xFBB1, - 0xFBD3, 0xFD3D, - 0xFD50, 0xFD8F, - 0xFD92, 0xFDC7, - 0xFDF0, 0xFDFB, - 0xFE70, 0xFE74, - 0xFE76, 0xFEFC, - 0xFF10, 0xFF19, - 0xFF21, 0xFF3A, - 0xFF41, 0xFF5A, - 0xFF66, 0xFFBE, - 0xFFC2, 0xFFC7, - 0xFFCA, 0xFFCF, - 0xFFD2, 0xFFD7, - 0xFFDA, 0xFFDC, - 0x10000, 0x1000B, - 0x1000D, 0x10026, - 0x10028, 0x1003A, - 0x1003C, 0x1003D, - 0x1003F, 0x1004D, - 0x10050, 0x1005D, - 0x10080, 0x100FA, - 0x10140, 0x10174, - 0x10280, 0x1029C, - 0x102A0, 0x102D0, - 0x10300, 0x1031F, - 0x1032D, 0x1034A, - 0x10350, 0x1037A, - 0x10380, 0x1039D, - 0x103A0, 0x103C3, - 0x103C8, 0x103CF, - 0x103D1, 0x103D5, - 0x10400, 0x1049D, - 0x104A0, 0x104A9, - 0x104B0, 0x104D3, - 0x104D8, 0x104FB, - 0x10500, 0x10527, - 0x10530, 0x10563, - 0x10570, 0x1057A, - 0x1057C, 0x1058A, - 0x1058C, 0x10592, - 0x10594, 0x10595, - 0x10597, 0x105A1, - 0x105A3, 0x105B1, - 0x105B3, 0x105B9, - 0x105BB, 0x105BC, - 0x10600, 0x10736, - 0x10740, 0x10755, - 0x10760, 0x10767, - 0x10780, 0x10785, - 0x10787, 0x107B0, - 0x107B2, 0x107BA, - 0x10800, 0x10805, - 0x10808, 0x10808, - 0x1080A, 0x10835, - 0x10837, 0x10838, - 0x1083C, 0x1083C, - 0x1083F, 0x10855, - 0x10860, 0x10876, - 0x10880, 0x1089E, - 0x108E0, 0x108F2, - 0x108F4, 0x108F5, - 0x10900, 0x10915, - 0x10920, 0x10939, - 0x10980, 0x109B7, - 0x109BE, 0x109BF, - 0x10A00, 0x10A03, - 0x10A05, 0x10A06, - 0x10A0C, 0x10A13, - 0x10A15, 0x10A17, - 0x10A19, 0x10A35, - 0x10A60, 0x10A7C, - 0x10A80, 0x10A9C, - 0x10AC0, 0x10AC7, - 0x10AC9, 0x10AE4, - 0x10B00, 0x10B35, - 0x10B40, 0x10B55, - 0x10B60, 0x10B72, - 0x10B80, 0x10B91, - 0x10C00, 0x10C48, - 0x10C80, 0x10CB2, - 0x10CC0, 0x10CF2, - 0x10D00, 0x10D27, - 0x10D30, 0x10D39, - 0x10E80, 0x10EA9, - 0x10EAB, 0x10EAC, - 0x10EB0, 0x10EB1, - 0x10F00, 0x10F1C, - 0x10F27, 0x10F27, - 0x10F30, 0x10F45, - 0x10F70, 0x10F81, - 0x10FB0, 0x10FC4, - 0x10FE0, 0x10FF6, - 0x11000, 0x11045, - 0x11066, 0x1106F, - 0x11071, 0x11075, - 0x11080, 0x110B8, - 0x110C2, 0x110C2, - 0x110D0, 0x110E8, - 0x110F0, 0x110F9, - 0x11100, 0x11132, - 0x11136, 0x1113F, - 0x11144, 0x11147, - 0x11150, 0x11172, - 0x11176, 0x11176, - 0x11180, 0x111BF, - 0x111C1, 0x111C4, - 0x111CE, 0x111DA, - 0x111DC, 0x111DC, - 0x11200, 0x11211, - 0x11213, 0x11234, - 0x11237, 0x11237, - 0x1123E, 0x11241, - 0x11280, 0x11286, - 0x11288, 0x11288, - 0x1128A, 0x1128D, - 0x1128F, 0x1129D, - 0x1129F, 0x112A8, - 0x112B0, 0x112E8, - 0x112F0, 0x112F9, - 0x11300, 0x11303, - 0x11305, 0x1130C, - 0x1130F, 0x11310, - 0x11313, 0x11328, - 0x1132A, 0x11330, - 0x11332, 0x11333, - 0x11335, 0x11339, - 0x1133D, 0x11344, - 0x11347, 0x11348, - 0x1134B, 0x1134C, - 0x11350, 0x11350, - 0x11357, 0x11357, - 0x1135D, 0x11363, - 0x11400, 0x11441, - 0x11443, 0x11445, - 0x11447, 0x1144A, - 0x11450, 0x11459, - 0x1145F, 0x11461, - 0x11480, 0x114C1, - 0x114C4, 0x114C5, - 0x114C7, 0x114C7, - 0x114D0, 0x114D9, - 0x11580, 0x115B5, - 0x115B8, 0x115BE, - 0x115D8, 0x115DD, - 0x11600, 0x1163E, - 0x11640, 0x11640, - 0x11644, 0x11644, - 0x11650, 0x11659, - 0x11680, 0x116B5, - 0x116B8, 0x116B8, - 0x116C0, 0x116C9, - 0x11700, 0x1171A, - 0x1171D, 0x1172A, - 0x11730, 0x11739, - 0x11740, 0x11746, - 0x11800, 0x11838, - 0x118A0, 0x118E9, - 0x118FF, 0x11906, - 0x11909, 0x11909, - 0x1190C, 0x11913, - 0x11915, 0x11916, - 0x11918, 0x11935, - 0x11937, 0x11938, - 0x1193B, 0x1193C, - 0x1193F, 0x11942, - 0x11950, 0x11959, - 0x119A0, 0x119A7, - 0x119AA, 0x119D7, - 0x119DA, 0x119DF, - 0x119E1, 0x119E1, - 0x119E3, 0x119E4, - 0x11A00, 0x11A32, - 0x11A35, 0x11A3E, - 0x11A50, 0x11A97, - 0x11A9D, 0x11A9D, - 0x11AB0, 0x11AF8, - 0x11C00, 0x11C08, - 0x11C0A, 0x11C36, - 0x11C38, 0x11C3E, - 0x11C40, 0x11C40, - 0x11C50, 0x11C59, - 0x11C72, 0x11C8F, - 0x11C92, 0x11CA7, - 0x11CA9, 0x11CB6, - 0x11D00, 0x11D06, - 0x11D08, 0x11D09, - 0x11D0B, 0x11D36, - 0x11D3A, 0x11D3A, - 0x11D3C, 0x11D3D, - 0x11D3F, 0x11D41, - 0x11D43, 0x11D43, - 0x11D46, 0x11D47, - 0x11D50, 0x11D59, - 0x11D60, 0x11D65, - 0x11D67, 0x11D68, - 0x11D6A, 0x11D8E, - 0x11D90, 0x11D91, - 0x11D93, 0x11D96, - 0x11D98, 0x11D98, - 0x11DA0, 0x11DA9, - 0x11EE0, 0x11EF6, - 0x11F00, 0x11F10, - 0x11F12, 0x11F3A, - 0x11F3E, 0x11F40, - 0x11F50, 0x11F59, - 0x11FB0, 0x11FB0, - 0x12000, 0x12399, - 0x12400, 0x1246E, - 0x12480, 0x12543, - 0x12F90, 0x12FF0, - 0x13000, 0x1342F, - 0x13441, 0x13446, - 0x14400, 0x14646, - 0x16800, 0x16A38, - 0x16A40, 0x16A5E, - 0x16A60, 0x16A69, - 0x16A70, 0x16ABE, - 0x16AC0, 0x16AC9, - 0x16AD0, 0x16AED, - 0x16B00, 0x16B2F, - 0x16B40, 0x16B43, - 0x16B50, 0x16B59, - 0x16B63, 0x16B77, - 0x16B7D, 0x16B8F, - 0x16E40, 0x16E7F, - 0x16F00, 0x16F4A, - 0x16F4F, 0x16F87, - 0x16F8F, 0x16F9F, - 0x16FE0, 0x16FE1, - 0x16FE3, 0x16FE3, - 0x16FF0, 0x16FF1, - 0x17000, 0x187F7, - 0x18800, 0x18CD5, - 0x18D00, 0x18D08, - 0x1AFF0, 0x1AFF3, - 0x1AFF5, 0x1AFFB, - 0x1AFFD, 0x1AFFE, - 0x1B000, 0x1B122, - 0x1B132, 0x1B132, - 0x1B150, 0x1B152, - 0x1B155, 0x1B155, - 0x1B164, 0x1B167, - 0x1B170, 0x1B2FB, - 0x1BC00, 0x1BC6A, - 0x1BC70, 0x1BC7C, - 0x1BC80, 0x1BC88, - 0x1BC90, 0x1BC99, - 0x1BC9E, 0x1BC9E, - 0x1D400, 0x1D454, - 0x1D456, 0x1D49C, - 0x1D49E, 0x1D49F, - 0x1D4A2, 0x1D4A2, - 0x1D4A5, 0x1D4A6, - 0x1D4A9, 0x1D4AC, - 0x1D4AE, 0x1D4B9, - 0x1D4BB, 0x1D4BB, - 0x1D4BD, 0x1D4C3, - 0x1D4C5, 0x1D505, - 0x1D507, 0x1D50A, - 0x1D50D, 0x1D514, - 0x1D516, 0x1D51C, - 0x1D51E, 0x1D539, - 0x1D53B, 0x1D53E, - 0x1D540, 0x1D544, - 0x1D546, 0x1D546, - 0x1D54A, 0x1D550, - 0x1D552, 0x1D6A5, - 0x1D6A8, 0x1D6C0, - 0x1D6C2, 0x1D6DA, - 0x1D6DC, 0x1D6FA, - 0x1D6FC, 0x1D714, - 0x1D716, 0x1D734, - 0x1D736, 0x1D74E, - 0x1D750, 0x1D76E, - 0x1D770, 0x1D788, - 0x1D78A, 0x1D7A8, - 0x1D7AA, 0x1D7C2, - 0x1D7C4, 0x1D7CB, - 0x1D7CE, 0x1D7FF, - 0x1DF00, 0x1DF1E, - 0x1DF25, 0x1DF2A, - 0x1E000, 0x1E006, - 0x1E008, 0x1E018, - 0x1E01B, 0x1E021, - 0x1E023, 0x1E024, - 0x1E026, 0x1E02A, - 0x1E030, 0x1E06D, - 0x1E08F, 0x1E08F, - 0x1E100, 0x1E12C, - 0x1E137, 0x1E13D, - 0x1E140, 0x1E149, - 0x1E14E, 0x1E14E, - 0x1E290, 0x1E2AD, - 0x1E2C0, 0x1E2EB, - 0x1E2F0, 0x1E2F9, - 0x1E4D0, 0x1E4EB, - 0x1E4F0, 0x1E4F9, - 0x1E7E0, 0x1E7E6, - 0x1E7E8, 0x1E7EB, - 0x1E7ED, 0x1E7EE, - 0x1E7F0, 0x1E7FE, - 0x1E800, 0x1E8C4, - 0x1E900, 0x1E943, - 0x1E947, 0x1E947, - 0x1E94B, 0x1E94B, - 0x1E950, 0x1E959, - 0x1EE00, 0x1EE03, - 0x1EE05, 0x1EE1F, - 0x1EE21, 0x1EE22, - 0x1EE24, 0x1EE24, - 0x1EE27, 0x1EE27, - 0x1EE29, 0x1EE32, - 0x1EE34, 0x1EE37, - 0x1EE39, 0x1EE39, - 0x1EE3B, 0x1EE3B, - 0x1EE42, 0x1EE42, - 0x1EE47, 0x1EE47, - 0x1EE49, 0x1EE49, - 0x1EE4B, 0x1EE4B, - 0x1EE4D, 0x1EE4F, - 0x1EE51, 0x1EE52, - 0x1EE54, 0x1EE54, - 0x1EE57, 0x1EE57, - 0x1EE59, 0x1EE59, - 0x1EE5B, 0x1EE5B, - 0x1EE5D, 0x1EE5D, - 0x1EE5F, 0x1EE5F, - 0x1EE61, 0x1EE62, - 0x1EE64, 0x1EE64, - 0x1EE67, 0x1EE6A, - 0x1EE6C, 0x1EE72, - 0x1EE74, 0x1EE77, - 0x1EE79, 0x1EE7C, - 0x1EE7E, 0x1EE7E, - 0x1EE80, 0x1EE89, - 0x1EE8B, 0x1EE9B, - 0x1EEA1, 0x1EEA3, - 0x1EEA5, 0x1EEA9, - 0x1EEAB, 0x1EEBB, - 0x1F130, 0x1F149, - 0x1F150, 0x1F169, - 0x1F170, 0x1F189, - 0x1FBF0, 0x1FBF9, - 0x20000, 0x2A6DF, - 0x2A700, 0x2B739, - 0x2B740, 0x2B81D, - 0x2B820, 0x2CEA1, - 0x2CEB0, 0x2EBE0, - 0x2F800, 0x2FA1D, - 0x30000, 0x3134A, - 0x31350, 0x323AF, -}; - -#define UNICODE_ISUPPER_CODEPOINTS_LENGTH 1296 -unicode_codepoint_t unicode_isupper_codepoints[UNICODE_ISUPPER_CODEPOINTS_LENGTH] = { - 0x100, 0x100, - 0x102, 0x102, - 0x104, 0x104, - 0x106, 0x106, - 0x108, 0x108, - 0x10A, 0x10A, - 0x10C, 0x10C, - 0x10E, 0x10E, - 0x110, 0x110, - 0x112, 0x112, - 0x114, 0x114, - 0x116, 0x116, - 0x118, 0x118, - 0x11A, 0x11A, - 0x11C, 0x11C, - 0x11E, 0x11E, - 0x120, 0x120, - 0x122, 0x122, - 0x124, 0x124, - 0x126, 0x126, - 0x128, 0x128, - 0x12A, 0x12A, - 0x12C, 0x12C, - 0x12E, 0x12E, - 0x130, 0x130, - 0x132, 0x132, - 0x134, 0x134, - 0x136, 0x136, - 0x139, 0x139, - 0x13B, 0x13B, - 0x13D, 0x13D, - 0x13F, 0x13F, - 0x141, 0x141, - 0x143, 0x143, - 0x145, 0x145, - 0x147, 0x147, - 0x14A, 0x14A, - 0x14C, 0x14C, - 0x14E, 0x14E, - 0x150, 0x150, - 0x152, 0x152, - 0x154, 0x154, - 0x156, 0x156, - 0x158, 0x158, - 0x15A, 0x15A, - 0x15C, 0x15C, - 0x15E, 0x15E, - 0x160, 0x160, - 0x162, 0x162, - 0x164, 0x164, - 0x166, 0x166, - 0x168, 0x168, - 0x16A, 0x16A, - 0x16C, 0x16C, - 0x16E, 0x16E, - 0x170, 0x170, - 0x172, 0x172, - 0x174, 0x174, - 0x176, 0x176, - 0x178, 0x179, - 0x17B, 0x17B, - 0x17D, 0x17D, - 0x181, 0x182, - 0x184, 0x184, - 0x186, 0x187, - 0x189, 0x18B, - 0x18E, 0x191, - 0x193, 0x194, - 0x196, 0x198, - 0x19C, 0x19D, - 0x19F, 0x1A0, - 0x1A2, 0x1A2, - 0x1A4, 0x1A4, - 0x1A6, 0x1A7, - 0x1A9, 0x1A9, - 0x1AC, 0x1AC, - 0x1AE, 0x1AF, - 0x1B1, 0x1B3, - 0x1B5, 0x1B5, - 0x1B7, 0x1B8, - 0x1BC, 0x1BC, - 0x1C4, 0x1C4, - 0x1C7, 0x1C7, - 0x1CA, 0x1CA, - 0x1CD, 0x1CD, - 0x1CF, 0x1CF, - 0x1D1, 0x1D1, - 0x1D3, 0x1D3, - 0x1D5, 0x1D5, - 0x1D7, 0x1D7, - 0x1D9, 0x1D9, - 0x1DB, 0x1DB, - 0x1DE, 0x1DE, - 0x1E0, 0x1E0, - 0x1E2, 0x1E2, - 0x1E4, 0x1E4, - 0x1E6, 0x1E6, - 0x1E8, 0x1E8, - 0x1EA, 0x1EA, - 0x1EC, 0x1EC, - 0x1EE, 0x1EE, - 0x1F1, 0x1F1, - 0x1F4, 0x1F4, - 0x1F6, 0x1F8, - 0x1FA, 0x1FA, - 0x1FC, 0x1FC, - 0x1FE, 0x1FE, - 0x200, 0x200, - 0x202, 0x202, - 0x204, 0x204, - 0x206, 0x206, - 0x208, 0x208, - 0x20A, 0x20A, - 0x20C, 0x20C, - 0x20E, 0x20E, - 0x210, 0x210, - 0x212, 0x212, - 0x214, 0x214, - 0x216, 0x216, - 0x218, 0x218, - 0x21A, 0x21A, - 0x21C, 0x21C, - 0x21E, 0x21E, - 0x220, 0x220, - 0x222, 0x222, - 0x224, 0x224, - 0x226, 0x226, - 0x228, 0x228, - 0x22A, 0x22A, - 0x22C, 0x22C, - 0x22E, 0x22E, - 0x230, 0x230, - 0x232, 0x232, - 0x23A, 0x23B, - 0x23D, 0x23E, - 0x241, 0x241, - 0x243, 0x246, - 0x248, 0x248, - 0x24A, 0x24A, - 0x24C, 0x24C, - 0x24E, 0x24E, - 0x370, 0x370, - 0x372, 0x372, - 0x376, 0x376, - 0x37F, 0x37F, - 0x386, 0x386, - 0x388, 0x38A, - 0x38C, 0x38C, - 0x38E, 0x38F, - 0x391, 0x3A1, - 0x3A3, 0x3AB, - 0x3CF, 0x3CF, - 0x3D2, 0x3D4, - 0x3D8, 0x3D8, - 0x3DA, 0x3DA, - 0x3DC, 0x3DC, - 0x3DE, 0x3DE, - 0x3E0, 0x3E0, - 0x3E2, 0x3E2, - 0x3E4, 0x3E4, - 0x3E6, 0x3E6, - 0x3E8, 0x3E8, - 0x3EA, 0x3EA, - 0x3EC, 0x3EC, - 0x3EE, 0x3EE, - 0x3F4, 0x3F4, - 0x3F7, 0x3F7, - 0x3F9, 0x3FA, - 0x3FD, 0x42F, - 0x460, 0x460, - 0x462, 0x462, - 0x464, 0x464, - 0x466, 0x466, - 0x468, 0x468, - 0x46A, 0x46A, - 0x46C, 0x46C, - 0x46E, 0x46E, - 0x470, 0x470, - 0x472, 0x472, - 0x474, 0x474, - 0x476, 0x476, - 0x478, 0x478, - 0x47A, 0x47A, - 0x47C, 0x47C, - 0x47E, 0x47E, - 0x480, 0x480, - 0x48A, 0x48A, - 0x48C, 0x48C, - 0x48E, 0x48E, - 0x490, 0x490, - 0x492, 0x492, - 0x494, 0x494, - 0x496, 0x496, - 0x498, 0x498, - 0x49A, 0x49A, - 0x49C, 0x49C, - 0x49E, 0x49E, - 0x4A0, 0x4A0, - 0x4A2, 0x4A2, - 0x4A4, 0x4A4, - 0x4A6, 0x4A6, - 0x4A8, 0x4A8, - 0x4AA, 0x4AA, - 0x4AC, 0x4AC, - 0x4AE, 0x4AE, - 0x4B0, 0x4B0, - 0x4B2, 0x4B2, - 0x4B4, 0x4B4, - 0x4B6, 0x4B6, - 0x4B8, 0x4B8, - 0x4BA, 0x4BA, - 0x4BC, 0x4BC, - 0x4BE, 0x4BE, - 0x4C0, 0x4C1, - 0x4C3, 0x4C3, - 0x4C5, 0x4C5, - 0x4C7, 0x4C7, - 0x4C9, 0x4C9, - 0x4CB, 0x4CB, - 0x4CD, 0x4CD, - 0x4D0, 0x4D0, - 0x4D2, 0x4D2, - 0x4D4, 0x4D4, - 0x4D6, 0x4D6, - 0x4D8, 0x4D8, - 0x4DA, 0x4DA, - 0x4DC, 0x4DC, - 0x4DE, 0x4DE, - 0x4E0, 0x4E0, - 0x4E2, 0x4E2, - 0x4E4, 0x4E4, - 0x4E6, 0x4E6, - 0x4E8, 0x4E8, - 0x4EA, 0x4EA, - 0x4EC, 0x4EC, - 0x4EE, 0x4EE, - 0x4F0, 0x4F0, - 0x4F2, 0x4F2, - 0x4F4, 0x4F4, - 0x4F6, 0x4F6, - 0x4F8, 0x4F8, - 0x4FA, 0x4FA, - 0x4FC, 0x4FC, - 0x4FE, 0x4FE, - 0x500, 0x500, - 0x502, 0x502, - 0x504, 0x504, - 0x506, 0x506, - 0x508, 0x508, - 0x50A, 0x50A, - 0x50C, 0x50C, - 0x50E, 0x50E, - 0x510, 0x510, - 0x512, 0x512, - 0x514, 0x514, - 0x516, 0x516, - 0x518, 0x518, - 0x51A, 0x51A, - 0x51C, 0x51C, - 0x51E, 0x51E, - 0x520, 0x520, - 0x522, 0x522, - 0x524, 0x524, - 0x526, 0x526, - 0x528, 0x528, - 0x52A, 0x52A, - 0x52C, 0x52C, - 0x52E, 0x52E, - 0x531, 0x556, - 0x10A0, 0x10C5, - 0x10C7, 0x10C7, - 0x10CD, 0x10CD, - 0x13A0, 0x13F5, - 0x1C90, 0x1CBA, - 0x1CBD, 0x1CBF, - 0x1E00, 0x1E00, - 0x1E02, 0x1E02, - 0x1E04, 0x1E04, - 0x1E06, 0x1E06, - 0x1E08, 0x1E08, - 0x1E0A, 0x1E0A, - 0x1E0C, 0x1E0C, - 0x1E0E, 0x1E0E, - 0x1E10, 0x1E10, - 0x1E12, 0x1E12, - 0x1E14, 0x1E14, - 0x1E16, 0x1E16, - 0x1E18, 0x1E18, - 0x1E1A, 0x1E1A, - 0x1E1C, 0x1E1C, - 0x1E1E, 0x1E1E, - 0x1E20, 0x1E20, - 0x1E22, 0x1E22, - 0x1E24, 0x1E24, - 0x1E26, 0x1E26, - 0x1E28, 0x1E28, - 0x1E2A, 0x1E2A, - 0x1E2C, 0x1E2C, - 0x1E2E, 0x1E2E, - 0x1E30, 0x1E30, - 0x1E32, 0x1E32, - 0x1E34, 0x1E34, - 0x1E36, 0x1E36, - 0x1E38, 0x1E38, - 0x1E3A, 0x1E3A, - 0x1E3C, 0x1E3C, - 0x1E3E, 0x1E3E, - 0x1E40, 0x1E40, - 0x1E42, 0x1E42, - 0x1E44, 0x1E44, - 0x1E46, 0x1E46, - 0x1E48, 0x1E48, - 0x1E4A, 0x1E4A, - 0x1E4C, 0x1E4C, - 0x1E4E, 0x1E4E, - 0x1E50, 0x1E50, - 0x1E52, 0x1E52, - 0x1E54, 0x1E54, - 0x1E56, 0x1E56, - 0x1E58, 0x1E58, - 0x1E5A, 0x1E5A, - 0x1E5C, 0x1E5C, - 0x1E5E, 0x1E5E, - 0x1E60, 0x1E60, - 0x1E62, 0x1E62, - 0x1E64, 0x1E64, - 0x1E66, 0x1E66, - 0x1E68, 0x1E68, - 0x1E6A, 0x1E6A, - 0x1E6C, 0x1E6C, - 0x1E6E, 0x1E6E, - 0x1E70, 0x1E70, - 0x1E72, 0x1E72, - 0x1E74, 0x1E74, - 0x1E76, 0x1E76, - 0x1E78, 0x1E78, - 0x1E7A, 0x1E7A, - 0x1E7C, 0x1E7C, - 0x1E7E, 0x1E7E, - 0x1E80, 0x1E80, - 0x1E82, 0x1E82, - 0x1E84, 0x1E84, - 0x1E86, 0x1E86, - 0x1E88, 0x1E88, - 0x1E8A, 0x1E8A, - 0x1E8C, 0x1E8C, - 0x1E8E, 0x1E8E, - 0x1E90, 0x1E90, - 0x1E92, 0x1E92, - 0x1E94, 0x1E94, - 0x1E9E, 0x1E9E, - 0x1EA0, 0x1EA0, - 0x1EA2, 0x1EA2, - 0x1EA4, 0x1EA4, - 0x1EA6, 0x1EA6, - 0x1EA8, 0x1EA8, - 0x1EAA, 0x1EAA, - 0x1EAC, 0x1EAC, - 0x1EAE, 0x1EAE, - 0x1EB0, 0x1EB0, - 0x1EB2, 0x1EB2, - 0x1EB4, 0x1EB4, - 0x1EB6, 0x1EB6, - 0x1EB8, 0x1EB8, - 0x1EBA, 0x1EBA, - 0x1EBC, 0x1EBC, - 0x1EBE, 0x1EBE, - 0x1EC0, 0x1EC0, - 0x1EC2, 0x1EC2, - 0x1EC4, 0x1EC4, - 0x1EC6, 0x1EC6, - 0x1EC8, 0x1EC8, - 0x1ECA, 0x1ECA, - 0x1ECC, 0x1ECC, - 0x1ECE, 0x1ECE, - 0x1ED0, 0x1ED0, - 0x1ED2, 0x1ED2, - 0x1ED4, 0x1ED4, - 0x1ED6, 0x1ED6, - 0x1ED8, 0x1ED8, - 0x1EDA, 0x1EDA, - 0x1EDC, 0x1EDC, - 0x1EDE, 0x1EDE, - 0x1EE0, 0x1EE0, - 0x1EE2, 0x1EE2, - 0x1EE4, 0x1EE4, - 0x1EE6, 0x1EE6, - 0x1EE8, 0x1EE8, - 0x1EEA, 0x1EEA, - 0x1EEC, 0x1EEC, - 0x1EEE, 0x1EEE, - 0x1EF0, 0x1EF0, - 0x1EF2, 0x1EF2, - 0x1EF4, 0x1EF4, - 0x1EF6, 0x1EF6, - 0x1EF8, 0x1EF8, - 0x1EFA, 0x1EFA, - 0x1EFC, 0x1EFC, - 0x1EFE, 0x1EFE, - 0x1F08, 0x1F0F, - 0x1F18, 0x1F1D, - 0x1F28, 0x1F2F, - 0x1F38, 0x1F3F, - 0x1F48, 0x1F4D, - 0x1F59, 0x1F59, - 0x1F5B, 0x1F5B, - 0x1F5D, 0x1F5D, - 0x1F5F, 0x1F5F, - 0x1F68, 0x1F6F, - 0x1FB8, 0x1FBB, - 0x1FC8, 0x1FCB, - 0x1FD8, 0x1FDB, - 0x1FE8, 0x1FEC, - 0x1FF8, 0x1FFB, - 0x2102, 0x2102, - 0x2107, 0x2107, - 0x210B, 0x210D, - 0x2110, 0x2112, - 0x2115, 0x2115, - 0x2119, 0x211D, - 0x2124, 0x2124, - 0x2126, 0x2126, - 0x2128, 0x2128, - 0x212A, 0x212D, - 0x2130, 0x2133, - 0x213E, 0x213F, - 0x2145, 0x2145, - 0x2160, 0x216F, - 0x2183, 0x2183, - 0x24B6, 0x24CF, - 0x2C00, 0x2C2F, - 0x2C60, 0x2C60, - 0x2C62, 0x2C64, - 0x2C67, 0x2C67, - 0x2C69, 0x2C69, - 0x2C6B, 0x2C6B, - 0x2C6D, 0x2C70, - 0x2C72, 0x2C72, - 0x2C75, 0x2C75, - 0x2C7E, 0x2C80, - 0x2C82, 0x2C82, - 0x2C84, 0x2C84, - 0x2C86, 0x2C86, - 0x2C88, 0x2C88, - 0x2C8A, 0x2C8A, - 0x2C8C, 0x2C8C, - 0x2C8E, 0x2C8E, - 0x2C90, 0x2C90, - 0x2C92, 0x2C92, - 0x2C94, 0x2C94, - 0x2C96, 0x2C96, - 0x2C98, 0x2C98, - 0x2C9A, 0x2C9A, - 0x2C9C, 0x2C9C, - 0x2C9E, 0x2C9E, - 0x2CA0, 0x2CA0, - 0x2CA2, 0x2CA2, - 0x2CA4, 0x2CA4, - 0x2CA6, 0x2CA6, - 0x2CA8, 0x2CA8, - 0x2CAA, 0x2CAA, - 0x2CAC, 0x2CAC, - 0x2CAE, 0x2CAE, - 0x2CB0, 0x2CB0, - 0x2CB2, 0x2CB2, - 0x2CB4, 0x2CB4, - 0x2CB6, 0x2CB6, - 0x2CB8, 0x2CB8, - 0x2CBA, 0x2CBA, - 0x2CBC, 0x2CBC, - 0x2CBE, 0x2CBE, - 0x2CC0, 0x2CC0, - 0x2CC2, 0x2CC2, - 0x2CC4, 0x2CC4, - 0x2CC6, 0x2CC6, - 0x2CC8, 0x2CC8, - 0x2CCA, 0x2CCA, - 0x2CCC, 0x2CCC, - 0x2CCE, 0x2CCE, - 0x2CD0, 0x2CD0, - 0x2CD2, 0x2CD2, - 0x2CD4, 0x2CD4, - 0x2CD6, 0x2CD6, - 0x2CD8, 0x2CD8, - 0x2CDA, 0x2CDA, - 0x2CDC, 0x2CDC, - 0x2CDE, 0x2CDE, - 0x2CE0, 0x2CE0, - 0x2CE2, 0x2CE2, - 0x2CEB, 0x2CEB, - 0x2CED, 0x2CED, - 0x2CF2, 0x2CF2, - 0xA640, 0xA640, - 0xA642, 0xA642, - 0xA644, 0xA644, - 0xA646, 0xA646, - 0xA648, 0xA648, - 0xA64A, 0xA64A, - 0xA64C, 0xA64C, - 0xA64E, 0xA64E, - 0xA650, 0xA650, - 0xA652, 0xA652, - 0xA654, 0xA654, - 0xA656, 0xA656, - 0xA658, 0xA658, - 0xA65A, 0xA65A, - 0xA65C, 0xA65C, - 0xA65E, 0xA65E, - 0xA660, 0xA660, - 0xA662, 0xA662, - 0xA664, 0xA664, - 0xA666, 0xA666, - 0xA668, 0xA668, - 0xA66A, 0xA66A, - 0xA66C, 0xA66C, - 0xA680, 0xA680, - 0xA682, 0xA682, - 0xA684, 0xA684, - 0xA686, 0xA686, - 0xA688, 0xA688, - 0xA68A, 0xA68A, - 0xA68C, 0xA68C, - 0xA68E, 0xA68E, - 0xA690, 0xA690, - 0xA692, 0xA692, - 0xA694, 0xA694, - 0xA696, 0xA696, - 0xA698, 0xA698, - 0xA69A, 0xA69A, - 0xA722, 0xA722, - 0xA724, 0xA724, - 0xA726, 0xA726, - 0xA728, 0xA728, - 0xA72A, 0xA72A, - 0xA72C, 0xA72C, - 0xA72E, 0xA72E, - 0xA732, 0xA732, - 0xA734, 0xA734, - 0xA736, 0xA736, - 0xA738, 0xA738, - 0xA73A, 0xA73A, - 0xA73C, 0xA73C, - 0xA73E, 0xA73E, - 0xA740, 0xA740, - 0xA742, 0xA742, - 0xA744, 0xA744, - 0xA746, 0xA746, - 0xA748, 0xA748, - 0xA74A, 0xA74A, - 0xA74C, 0xA74C, - 0xA74E, 0xA74E, - 0xA750, 0xA750, - 0xA752, 0xA752, - 0xA754, 0xA754, - 0xA756, 0xA756, - 0xA758, 0xA758, - 0xA75A, 0xA75A, - 0xA75C, 0xA75C, - 0xA75E, 0xA75E, - 0xA760, 0xA760, - 0xA762, 0xA762, - 0xA764, 0xA764, - 0xA766, 0xA766, - 0xA768, 0xA768, - 0xA76A, 0xA76A, - 0xA76C, 0xA76C, - 0xA76E, 0xA76E, - 0xA779, 0xA779, - 0xA77B, 0xA77B, - 0xA77D, 0xA77E, - 0xA780, 0xA780, - 0xA782, 0xA782, - 0xA784, 0xA784, - 0xA786, 0xA786, - 0xA78B, 0xA78B, - 0xA78D, 0xA78D, - 0xA790, 0xA790, - 0xA792, 0xA792, - 0xA796, 0xA796, - 0xA798, 0xA798, - 0xA79A, 0xA79A, - 0xA79C, 0xA79C, - 0xA79E, 0xA79E, - 0xA7A0, 0xA7A0, - 0xA7A2, 0xA7A2, - 0xA7A4, 0xA7A4, - 0xA7A6, 0xA7A6, - 0xA7A8, 0xA7A8, - 0xA7AA, 0xA7AE, - 0xA7B0, 0xA7B4, - 0xA7B6, 0xA7B6, - 0xA7B8, 0xA7B8, - 0xA7BA, 0xA7BA, - 0xA7BC, 0xA7BC, - 0xA7BE, 0xA7BE, - 0xA7C0, 0xA7C0, - 0xA7C2, 0xA7C2, - 0xA7C4, 0xA7C7, - 0xA7C9, 0xA7C9, - 0xA7D0, 0xA7D0, - 0xA7D6, 0xA7D6, - 0xA7D8, 0xA7D8, - 0xA7F5, 0xA7F5, - 0xFF21, 0xFF3A, - 0x10400, 0x10427, - 0x104B0, 0x104D3, - 0x10570, 0x1057A, - 0x1057C, 0x1058A, - 0x1058C, 0x10592, - 0x10594, 0x10595, - 0x10C80, 0x10CB2, - 0x118A0, 0x118BF, - 0x16E40, 0x16E5F, - 0x1D400, 0x1D419, - 0x1D434, 0x1D44D, - 0x1D468, 0x1D481, - 0x1D49C, 0x1D49C, - 0x1D49E, 0x1D49F, - 0x1D4A2, 0x1D4A2, - 0x1D4A5, 0x1D4A6, - 0x1D4A9, 0x1D4AC, - 0x1D4AE, 0x1D4B5, - 0x1D4D0, 0x1D4E9, - 0x1D504, 0x1D505, - 0x1D507, 0x1D50A, - 0x1D50D, 0x1D514, - 0x1D516, 0x1D51C, - 0x1D538, 0x1D539, - 0x1D53B, 0x1D53E, - 0x1D540, 0x1D544, - 0x1D546, 0x1D546, - 0x1D54A, 0x1D550, - 0x1D56C, 0x1D585, - 0x1D5A0, 0x1D5B9, - 0x1D5D4, 0x1D5ED, - 0x1D608, 0x1D621, - 0x1D63C, 0x1D655, - 0x1D670, 0x1D689, - 0x1D6A8, 0x1D6C0, - 0x1D6E2, 0x1D6FA, - 0x1D71C, 0x1D734, - 0x1D756, 0x1D76E, - 0x1D790, 0x1D7A8, - 0x1D7CA, 0x1D7CA, - 0x1E900, 0x1E921, - 0x1F130, 0x1F149, - 0x1F150, 0x1F169, - 0x1F170, 0x1F189, -}; - -static bool -unicode_codepoint_match(unicode_codepoint_t codepoint, unicode_codepoint_t *codepoints, size_t size) { - size_t start = 0; - size_t end = size; - - while (start < end) { - size_t middle = start + (end - start) / 2; - if ((middle % 2) != 0) middle--; - - if (codepoint >= codepoints[middle] && codepoint <= codepoints[middle + 1]) { - return true; - } - - if (codepoint < codepoints[middle]) { - end = middle; - } else { - start = middle + 2; - } - } - - return false; -} - -static unicode_codepoint_t -utf_8_codepoint(const unsigned char *c, size_t *width) { - if ((c[0] >> 7) == 0) { - // 0xxxxxxx - *width = 1; - return (unicode_codepoint_t) c[0]; - } - if (((c[0] >> 5) == 0x6) && ((c[1] >> 6) == 0x2)) { - // 110xxxxx 10xxxxxx - *width = 2; - return (unicode_codepoint_t) (((c[0] & 0x1F) << 6) | (c[1] & 0x3F)); - } - if (((c[0] >> 4) == 0xE) && ((c[1] >> 6) == 0x2) && ((c[2] >> 6) == 0x2)) { - // 1110xxxx 10xxxxxx 10xxxxxx - *width = 3; - return (unicode_codepoint_t) (((c[0] & 0x0F) << 12) | ((c[1] & 0x3F) << 6) | (c[2] & 0x3F)); - } - if (((c[0] >> 3) == 0x1E) && ((c[1] >> 6) == 0x2) && ((c[2] >> 6) == 0x2) && ((c[3] >> 6) == 0x2)) { - // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - *width = 4; - return (unicode_codepoint_t) (((c[0] & 0x07) << 18) | ((c[1] & 0x3F) << 12) | ((c[2] & 0x3F) << 6) | (c[3] & 0x3F)); - } - *width = 0; - return 0; -} - -size_t -yp_encoding_utf_8_char_width(const char *c) { - size_t width; - const unsigned char *uc = (const unsigned char *) c; - - utf_8_codepoint(uc, &width); - return width; -} - -size_t -yp_encoding_utf_8_alpha_char(const char *c) { - size_t width; - unicode_codepoint_t codepoint = utf_8_codepoint((const unsigned char *) c, &width); - - if (codepoint <= 0xFF) { - return (yp_encoding_unicode_table[(unsigned char) codepoint] & YP_ENCODING_ALPHABETIC_BIT) ? width : 0; - } else { - return unicode_codepoint_match(codepoint, unicode_alpha_codepoints, UNICODE_ALPHA_CODEPOINTS_LENGTH) ? width : 0; - } -} - -size_t -yp_encoding_utf_8_alnum_char(const char *c) { - size_t width; - unicode_codepoint_t codepoint = utf_8_codepoint((const unsigned char *) c, &width); - - if (codepoint <= 0xFF) { - return (yp_encoding_unicode_table[(unsigned char) codepoint] & (YP_ENCODING_ALPHANUMERIC_BIT)) ? width : 0; - } else { - return unicode_codepoint_match(codepoint, unicode_alnum_codepoints, UNICODE_ALNUM_CODEPOINTS_LENGTH) ? width : 0; - } -} - -bool -yp_encoding_utf_8_isupper_char(const char *c) { - size_t width; - unicode_codepoint_t codepoint = utf_8_codepoint((const unsigned char *) c, &width); - - if (codepoint <= 0xFF) { - return (yp_encoding_unicode_table[(unsigned char) codepoint] & YP_ENCODING_UPPERCASE_BIT) ? true : false; - } else { - return unicode_codepoint_match(codepoint, unicode_isupper_codepoints, UNICODE_ISUPPER_CODEPOINTS_LENGTH) ? true : false; - } -} - -#undef UNICODE_ALPHA_CODEPOINTS_LENGTH -#undef UNICODE_ALNUM_CODEPOINTS_LENGTH -#undef UNICODE_ISUPPER_CODEPOINTS_LENGTH diff --git a/src/main/c/yarp/src/yarp/src/enc/windows_1251.c b/src/main/c/yarp/src/yarp/src/enc/windows_1251.c deleted file mode 100644 index 5fd3624a8b74..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/windows_1251.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding windows-1251 character. -static unsigned char yp_encoding_windows_1251_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 7, 7, 0, 3, 0, 0, 0, 0, 0, 0, 7, 0, 7, 7, 7, 7, // 8x - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 3, 3, 3, // 9x - 0, 7, 3, 7, 0, 7, 0, 0, 7, 0, 7, 0, 0, 0, 0, 7, // Ax - 0, 0, 7, 3, 3, 3, 0, 0, 3, 0, 3, 0, 3, 7, 3, 3, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Fx -}; - -size_t -yp_encoding_windows_1251_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_windows_1251_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_windows_1251_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_windows_1251_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_windows_1251_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_windows_1251_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/windows_1252.c b/src/main/c/yarp/src/yarp/src/enc/windows_1252.c deleted file mode 100644 index ae076fd0a924..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/windows_1252.c +++ /dev/null @@ -1,41 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -// Each element of the following table contains a bitfield that indicates a -// piece of information about the corresponding windows-1252 character. -static unsigned char yp_encoding_windows_1252_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 2, 2, 2, 2, 2, 2, 2, 2, 2, 2, 0, 0, 0, 0, 0, 0, // 3x - 0, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // 4x - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 0, 0, 0, 0, 0, // 5x - 0, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // 6x - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 7, 0, 7, 0, 7, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 3, 0, 3, 7, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Ax - 0, 0, 0, 0, 0, 3, 0, 0, 0, 0, 3, 0, 0, 0, 0, 0, // Bx - 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, 7, // Cx - 7, 7, 7, 7, 7, 7, 7, 0, 7, 7, 7, 7, 7, 7, 7, 3, // Dx - 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, // Ex - 3, 3, 3, 3, 3, 3, 3, 0, 3, 3, 3, 3, 3, 3, 3, 3, // Fx -}; - -size_t -yp_encoding_windows_1252_alpha_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_windows_1252_table[v] & YP_ENCODING_ALPHABETIC_BIT) ? 1 : 0; -} - -size_t -yp_encoding_windows_1252_alnum_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_windows_1252_table[v] & YP_ENCODING_ALPHANUMERIC_BIT) ? 1 : 0; -} - -bool -yp_encoding_windows_1252_isupper_char(const char *c) { - const unsigned char v = (const unsigned char) *c; - return (yp_encoding_windows_1252_table[v] & YP_ENCODING_UPPERCASE_BIT) ? true : false; -} diff --git a/src/main/c/yarp/src/yarp/src/enc/windows_31j.c b/src/main/c/yarp/src/yarp/src/enc/windows_31j.c deleted file mode 100644 index fb08ea14f303..000000000000 --- a/src/main/c/yarp/src/yarp/src/enc/windows_31j.c +++ /dev/null @@ -1,73 +0,0 @@ -#include "yarp/enc/yp_encoding.h" - -typedef uint32_t windows_31j_codepoint_t; - -static windows_31j_codepoint_t -windows_31j_codepoint(const char *c, size_t *width) { - const unsigned char *uc = (const unsigned char *) c; - - // These are the single byte characters. - if (*uc < 0x80 || (*uc >= 0xA1 && *uc <= 0xDF)) { - *width = 1; - return *uc; - } - - // These are the double byte characters. - if ( - ((uc[0] >= 0x81 && uc[0] <= 0x9F) || (uc[0] >= 0xE0 && uc[0] <= 0xFC)) && - (uc[1] >= 0x40 && uc[1] <= 0xFC) - ) { - *width = 2; - return (windows_31j_codepoint_t) (uc[0] << 8 | uc[1]); - } - - *width = 0; - return 0; -} - -size_t -yp_encoding_windows_31j_char_width(const char *c) { - size_t width; - windows_31j_codepoint(c, &width); - - return width; -} - -size_t -yp_encoding_windows_31j_alpha_char(const char *c) { - size_t width; - windows_31j_codepoint_t codepoint = windows_31j_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_alpha_char(&value); - } else { - return 0; - } -} - -size_t -yp_encoding_windows_31j_alnum_char(const char *c) { - size_t width; - windows_31j_codepoint_t codepoint = windows_31j_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_alnum_char(&value); - } else { - return 0; - } -} - -bool -yp_encoding_windows_31j_isupper_char(const char *c) { - size_t width; - windows_31j_codepoint_t codepoint = windows_31j_codepoint(c, &width); - - if (width == 1) { - const char value = (const char) codepoint; - return yp_encoding_ascii_isupper_char(&value); - } else { - return false; - } -} diff --git a/src/main/c/yarp/src/yarp/src/missing.c b/src/main/c/yarp/src/yarp/src/missing.c deleted file mode 100644 index e6fb1a2af759..000000000000 --- a/src/main/c/yarp/src/yarp/src/missing.c +++ /dev/null @@ -1,34 +0,0 @@ -#include "yarp/missing.h" - -const char * -yp_strnstr(const char *haystack, const char *needle, size_t length) { - size_t needle_length = strnlen(needle, length); - if (needle_length > length) return NULL; - - const char *haystack_limit = haystack + length - needle_length + 1; - - while ((haystack = memchr(haystack, needle[0], (size_t) (haystack_limit - haystack))) != NULL) { - if (!strncmp(haystack, needle, needle_length)) return haystack; - haystack++; - } - - return NULL; -} - -int -yp_strncasecmp(const char *string1, const char *string2, size_t length) { - size_t offset = 0; - int difference = 0; - - while (offset < length && string1[offset] != '\0') { - if (string2[offset] == '\0') return string1[offset]; - - unsigned char left = (unsigned char) string1[offset]; - unsigned char right = (unsigned char) string2[offset]; - - if ((difference = tolower(left) - tolower(right)) != 0) return difference; - offset++; - } - - return difference; -} diff --git a/src/main/c/yarp/src/yarp/src/node.c b/src/main/c/yarp/src/yarp/src/node.c deleted file mode 100644 index a07045e86726..000000000000 --- a/src/main/c/yarp/src/yarp/src/node.c +++ /dev/null @@ -1,1394 +0,0 @@ -/******************************************************************************/ -/* This file is generated by the bin/template script and should not be */ -/* modified manually. See */ -/* bin/templates/src/node.c.erb */ -/* if you are looking to modify the */ -/* template */ -/******************************************************************************/ -#include "yarp/node.h" - -// Clear the node but preserves the location. -void yp_node_clear(yp_node_t *node) { - yp_location_t location = node->location; - memset(node, 0, sizeof(yp_node_t)); - node->location = location; -} - -// Initialize a yp_token_list_t with its default values. -void -yp_token_list_init(yp_token_list_t *token_list) { - *token_list = (yp_token_list_t) YP_EMPTY_TOKEN_LIST; -} - -// Calculate the size of the token list in bytes. -static size_t -yp_token_list_memsize(yp_token_list_t *token_list) { - return sizeof(yp_token_list_t) + (token_list->capacity * sizeof(yp_token_t)); -} - -// Append a token to the given list. -void -yp_token_list_append(yp_token_list_t *token_list, const yp_token_t *token) { - if (token_list->size == token_list->capacity) { - token_list->capacity = token_list->capacity == 0 ? 1 : token_list->capacity * 2; - token_list->tokens = realloc(token_list->tokens, sizeof(yp_token_t) * token_list->capacity); - } - token_list->tokens[token_list->size++] = *token; -} - -// Checks if the current token list includes the given token. -bool -yp_token_list_includes(yp_token_list_t *token_list, const yp_token_t *token) { - size_t length = (size_t) (token->end - token->start); - - for (size_t index = 0; index < token_list->size; index++) { - yp_token_t current_token = token_list->tokens[index]; - size_t token_length = (size_t) (current_token.end - current_token.start); - - if ((token_length == length) && (memcmp(current_token.start, token->start, length) == 0)) { - return true; - } - } - return false; -} - -// Free the memory associated with the token list. -static void -yp_token_list_free(yp_token_list_t *token_list) { - if (token_list->tokens != NULL) { - free(token_list->tokens); - } -} - -static void -yp_node_memsize_node(yp_node_t *node, yp_memsize_t *memsize); - -// Initiailize a list of nodes. -void -yp_node_list_init(yp_node_list_t *node_list) { - *node_list = (yp_node_list_t) YP_EMPTY_NODE_LIST; -} - -// Calculate the size of the node list in bytes. -static size_t -yp_node_list_memsize(yp_node_list_t *node_list, yp_memsize_t *memsize) { - size_t size = sizeof(yp_node_list_t) + (node_list->capacity * sizeof(yp_node_t *)); - for (size_t index = 0; index < node_list->size; index++) { - yp_node_memsize_node(node_list->nodes[index], memsize); - } - return size; -} - -// Append a new node onto the end of the node list. -void -yp_node_list_append(yp_node_list_t *list, yp_node_t *node) { - if (list->size == list->capacity) { - list->capacity = list->capacity == 0 ? 4 : list->capacity * 2; - list->nodes = realloc(list->nodes, list->capacity * sizeof(yp_node_t *)); - } - list->nodes[list->size++] = node; -} - -__attribute__((__visibility__("default"))) void -yp_node_destroy(yp_parser_t *parser, yp_node_t *node); - -// Deallocate the inner memory of a list of nodes. The parser argument is not -// used, but is here for the future possibility of pre-allocating memory pools. -static void -yp_node_list_free(yp_parser_t *parser, yp_node_list_t *list) { - if (list->capacity > 0) { - for (size_t index = 0; index < list->size; index++) { - yp_node_destroy(parser, list->nodes[index]); - } - free(list->nodes); - } -} - -// Deallocate the space for a yp_node_t. Similarly to yp_node_alloc, we're not -// using the parser argument, but it's there to allow for the future possibility -// of pre-allocating larger memory pools. -__attribute__((__visibility__("default"))) void -yp_node_destroy(yp_parser_t *parser, yp_node_t *node) { - switch (node->type) { - case YP_NODE_ALIAS_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_alias_node_t *)node)->new_name); - yp_node_destroy(parser, (yp_node_t *)((yp_alias_node_t *)node)->old_name); - break; - case YP_NODE_ALTERNATION_PATTERN_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->left); - yp_node_destroy(parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->right); - break; - case YP_NODE_AND_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_and_node_t *)node)->left); - yp_node_destroy(parser, (yp_node_t *)((yp_and_node_t *)node)->right); - break; - case YP_NODE_ARGUMENTS_NODE: - yp_node_list_free(parser, &((yp_arguments_node_t *)node)->arguments); - break; - case YP_NODE_ARRAY_NODE: - yp_node_list_free(parser, &((yp_array_node_t *)node)->elements); - break; - case YP_NODE_ARRAY_PATTERN_NODE: - if (((yp_array_pattern_node_t *)node)->constant != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->constant); - } - yp_node_list_free(parser, &((yp_array_pattern_node_t *)node)->requireds); - if (((yp_array_pattern_node_t *)node)->rest != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->rest); - } - yp_node_list_free(parser, &((yp_array_pattern_node_t *)node)->posts); - break; - case YP_NODE_ASSOC_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_assoc_node_t *)node)->key); - if (((yp_assoc_node_t *)node)->value != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_assoc_node_t *)node)->value); - } - break; - case YP_NODE_ASSOC_SPLAT_NODE: - if (((yp_assoc_splat_node_t *)node)->value != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_assoc_splat_node_t *)node)->value); - } - break; - case YP_NODE_BEGIN_NODE: - if (((yp_begin_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_begin_node_t *)node)->statements); - } - if (((yp_begin_node_t *)node)->rescue_clause != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_begin_node_t *)node)->rescue_clause); - } - if (((yp_begin_node_t *)node)->else_clause != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_begin_node_t *)node)->else_clause); - } - if (((yp_begin_node_t *)node)->ensure_clause != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_begin_node_t *)node)->ensure_clause); - } - break; - case YP_NODE_BLOCK_ARGUMENT_NODE: - if (((yp_block_argument_node_t *)node)->expression != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_block_argument_node_t *)node)->expression); - } - break; - case YP_NODE_BLOCK_NODE: - yp_token_list_free(&((yp_block_node_t *)node)->locals); - if (((yp_block_node_t *)node)->parameters != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_block_node_t *)node)->parameters); - } - if (((yp_block_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_block_node_t *)node)->statements); - } - break; - case YP_NODE_BLOCK_PARAMETER_NODE: - break; - case YP_NODE_BLOCK_PARAMETERS_NODE: - if (((yp_block_parameters_node_t *)node)->parameters != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_block_parameters_node_t *)node)->parameters); - } - yp_token_list_free(&((yp_block_parameters_node_t *)node)->locals); - break; - case YP_NODE_BREAK_NODE: - if (((yp_break_node_t *)node)->arguments != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_break_node_t *)node)->arguments); - } - break; - case YP_NODE_CALL_NODE: - if (((yp_call_node_t *)node)->receiver != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_call_node_t *)node)->receiver); - } - if (((yp_call_node_t *)node)->arguments != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_call_node_t *)node)->arguments); - } - if (((yp_call_node_t *)node)->block != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_call_node_t *)node)->block); - } - yp_string_free(&((yp_call_node_t *)node)->name); - break; - case YP_NODE_CAPTURE_PATTERN_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->value); - yp_node_destroy(parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->target); - break; - case YP_NODE_CASE_NODE: - if (((yp_case_node_t *)node)->predicate != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_case_node_t *)node)->predicate); - } - yp_node_list_free(parser, &((yp_case_node_t *)node)->conditions); - if (((yp_case_node_t *)node)->consequent != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_case_node_t *)node)->consequent); - } - break; - case YP_NODE_CLASS_NODE: - yp_token_list_free(&((yp_class_node_t *)node)->locals); - yp_node_destroy(parser, (yp_node_t *)((yp_class_node_t *)node)->constant_path); - if (((yp_class_node_t *)node)->superclass != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_class_node_t *)node)->superclass); - } - if (((yp_class_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_class_node_t *)node)->statements); - } - break; - case YP_NODE_CLASS_VARIABLE_READ_NODE: - break; - case YP_NODE_CLASS_VARIABLE_WRITE_NODE: - if (((yp_class_variable_write_node_t *)node)->value != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_class_variable_write_node_t *)node)->value); - } - break; - case YP_NODE_CONSTANT_PATH_NODE: - if (((yp_constant_path_node_t *)node)->parent != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_node_t *)node)->parent); - } - yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_node_t *)node)->child); - break; - case YP_NODE_CONSTANT_PATH_WRITE_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->target); - if (((yp_constant_path_write_node_t *)node)->value != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->value); - } - break; - case YP_NODE_CONSTANT_READ_NODE: - break; - case YP_NODE_DEF_NODE: - if (((yp_def_node_t *)node)->receiver != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_def_node_t *)node)->receiver); - } - if (((yp_def_node_t *)node)->parameters != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_def_node_t *)node)->parameters); - } - if (((yp_def_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_def_node_t *)node)->statements); - } - yp_token_list_free(&((yp_def_node_t *)node)->locals); - break; - case YP_NODE_DEFINED_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_defined_node_t *)node)->value); - break; - case YP_NODE_ELSE_NODE: - if (((yp_else_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_else_node_t *)node)->statements); - } - break; - case YP_NODE_ENSURE_NODE: - if (((yp_ensure_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_ensure_node_t *)node)->statements); - } - break; - case YP_NODE_FALSE_NODE: - break; - case YP_NODE_FIND_PATTERN_NODE: - if (((yp_find_pattern_node_t *)node)->constant != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->constant); - } - yp_node_destroy(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->left); - yp_node_list_free(parser, &((yp_find_pattern_node_t *)node)->requireds); - yp_node_destroy(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->right); - break; - case YP_NODE_FLOAT_NODE: - break; - case YP_NODE_FOR_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_for_node_t *)node)->index); - yp_node_destroy(parser, (yp_node_t *)((yp_for_node_t *)node)->collection); - if (((yp_for_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_for_node_t *)node)->statements); - } - break; - case YP_NODE_FORWARDING_ARGUMENTS_NODE: - break; - case YP_NODE_FORWARDING_PARAMETER_NODE: - break; - case YP_NODE_FORWARDING_SUPER_NODE: - if (((yp_forwarding_super_node_t *)node)->block != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_forwarding_super_node_t *)node)->block); - } - break; - case YP_NODE_GLOBAL_VARIABLE_READ_NODE: - break; - case YP_NODE_GLOBAL_VARIABLE_WRITE_NODE: - if (((yp_global_variable_write_node_t *)node)->value != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_global_variable_write_node_t *)node)->value); - } - break; - case YP_NODE_HASH_NODE: - yp_node_list_free(parser, &((yp_hash_node_t *)node)->elements); - break; - case YP_NODE_HASH_PATTERN_NODE: - if (((yp_hash_pattern_node_t *)node)->constant != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->constant); - } - yp_node_list_free(parser, &((yp_hash_pattern_node_t *)node)->assocs); - if (((yp_hash_pattern_node_t *)node)->kwrest != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->kwrest); - } - break; - case YP_NODE_IF_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_if_node_t *)node)->predicate); - if (((yp_if_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_if_node_t *)node)->statements); - } - if (((yp_if_node_t *)node)->consequent != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_if_node_t *)node)->consequent); - } - break; - case YP_NODE_IMAGINARY_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_imaginary_node_t *)node)->numeric); - break; - case YP_NODE_IN_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_in_node_t *)node)->pattern); - if (((yp_in_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_in_node_t *)node)->statements); - } - break; - case YP_NODE_INSTANCE_VARIABLE_READ_NODE: - break; - case YP_NODE_INSTANCE_VARIABLE_WRITE_NODE: - if (((yp_instance_variable_write_node_t *)node)->value != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_instance_variable_write_node_t *)node)->value); - } - break; - case YP_NODE_INTEGER_NODE: - break; - case YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE: - yp_node_list_free(parser, &((yp_interpolated_regular_expression_node_t *)node)->parts); - break; - case YP_NODE_INTERPOLATED_STRING_NODE: - yp_node_list_free(parser, &((yp_interpolated_string_node_t *)node)->parts); - break; - case YP_NODE_INTERPOLATED_SYMBOL_NODE: - yp_node_list_free(parser, &((yp_interpolated_symbol_node_t *)node)->parts); - break; - case YP_NODE_INTERPOLATED_X_STRING_NODE: - yp_node_list_free(parser, &((yp_interpolated_x_string_node_t *)node)->parts); - break; - case YP_NODE_KEYWORD_HASH_NODE: - yp_node_list_free(parser, &((yp_keyword_hash_node_t *)node)->elements); - break; - case YP_NODE_KEYWORD_PARAMETER_NODE: - if (((yp_keyword_parameter_node_t *)node)->value != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_keyword_parameter_node_t *)node)->value); - } - break; - case YP_NODE_KEYWORD_REST_PARAMETER_NODE: - break; - case YP_NODE_LAMBDA_NODE: - yp_token_list_free(&((yp_lambda_node_t *)node)->locals); - if (((yp_lambda_node_t *)node)->parameters != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_lambda_node_t *)node)->parameters); - } - if (((yp_lambda_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_lambda_node_t *)node)->statements); - } - break; - case YP_NODE_LOCAL_VARIABLE_READ_NODE: - break; - case YP_NODE_LOCAL_VARIABLE_WRITE_NODE: - if (((yp_local_variable_write_node_t *)node)->value != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_local_variable_write_node_t *)node)->value); - } - break; - case YP_NODE_MATCH_PREDICATE_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->value); - yp_node_destroy(parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->pattern); - break; - case YP_NODE_MATCH_REQUIRED_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_match_required_node_t *)node)->value); - yp_node_destroy(parser, (yp_node_t *)((yp_match_required_node_t *)node)->pattern); - break; - case YP_NODE_MISSING_NODE: - break; - case YP_NODE_MODULE_NODE: - yp_token_list_free(&((yp_module_node_t *)node)->locals); - yp_node_destroy(parser, (yp_node_t *)((yp_module_node_t *)node)->constant_path); - if (((yp_module_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_module_node_t *)node)->statements); - } - break; - case YP_NODE_MULTI_WRITE_NODE: - yp_node_list_free(parser, &((yp_multi_write_node_t *)node)->targets); - if (((yp_multi_write_node_t *)node)->value != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_multi_write_node_t *)node)->value); - } - break; - case YP_NODE_NEXT_NODE: - if (((yp_next_node_t *)node)->arguments != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_next_node_t *)node)->arguments); - } - break; - case YP_NODE_NIL_NODE: - break; - case YP_NODE_NO_KEYWORDS_PARAMETER_NODE: - break; - case YP_NODE_OPERATOR_AND_ASSIGNMENT_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_operator_and_assignment_node_t *)node)->target); - yp_node_destroy(parser, (yp_node_t *)((yp_operator_and_assignment_node_t *)node)->value); - break; - case YP_NODE_OPERATOR_ASSIGNMENT_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_operator_assignment_node_t *)node)->target); - yp_node_destroy(parser, (yp_node_t *)((yp_operator_assignment_node_t *)node)->value); - break; - case YP_NODE_OPERATOR_OR_ASSIGNMENT_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_operator_or_assignment_node_t *)node)->target); - yp_node_destroy(parser, (yp_node_t *)((yp_operator_or_assignment_node_t *)node)->value); - break; - case YP_NODE_OPTIONAL_PARAMETER_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_optional_parameter_node_t *)node)->value); - break; - case YP_NODE_OR_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_or_node_t *)node)->left); - yp_node_destroy(parser, (yp_node_t *)((yp_or_node_t *)node)->right); - break; - case YP_NODE_PARAMETERS_NODE: - yp_node_list_free(parser, &((yp_parameters_node_t *)node)->requireds); - yp_node_list_free(parser, &((yp_parameters_node_t *)node)->optionals); - yp_node_list_free(parser, &((yp_parameters_node_t *)node)->posts); - if (((yp_parameters_node_t *)node)->rest != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_parameters_node_t *)node)->rest); - } - yp_node_list_free(parser, &((yp_parameters_node_t *)node)->keywords); - if (((yp_parameters_node_t *)node)->keyword_rest != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_parameters_node_t *)node)->keyword_rest); - } - if (((yp_parameters_node_t *)node)->block != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_parameters_node_t *)node)->block); - } - break; - case YP_NODE_PARENTHESES_NODE: - if (((yp_parentheses_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_parentheses_node_t *)node)->statements); - } - break; - case YP_NODE_PINNED_EXPRESSION_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_pinned_expression_node_t *)node)->expression); - break; - case YP_NODE_PINNED_VARIABLE_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_pinned_variable_node_t *)node)->variable); - break; - case YP_NODE_POST_EXECUTION_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_post_execution_node_t *)node)->statements); - break; - case YP_NODE_PRE_EXECUTION_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_pre_execution_node_t *)node)->statements); - break; - case YP_NODE_PROGRAM_NODE: - yp_token_list_free(&((yp_program_node_t *)node)->locals); - yp_node_destroy(parser, (yp_node_t *)((yp_program_node_t *)node)->statements); - break; - case YP_NODE_RANGE_NODE: - if (((yp_range_node_t *)node)->left != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_range_node_t *)node)->left); - } - if (((yp_range_node_t *)node)->right != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_range_node_t *)node)->right); - } - break; - case YP_NODE_RATIONAL_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_rational_node_t *)node)->numeric); - break; - case YP_NODE_REDO_NODE: - break; - case YP_NODE_REGULAR_EXPRESSION_NODE: - yp_string_free(&((yp_regular_expression_node_t *)node)->unescaped); - break; - case YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE: - yp_node_list_free(parser, &((yp_required_destructured_parameter_node_t *)node)->parameters); - break; - case YP_NODE_REQUIRED_PARAMETER_NODE: - break; - case YP_NODE_RESCUE_MODIFIER_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->expression); - yp_node_destroy(parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->rescue_expression); - break; - case YP_NODE_RESCUE_NODE: - yp_node_list_free(parser, &((yp_rescue_node_t *)node)->exceptions); - if (((yp_rescue_node_t *)node)->exception != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_rescue_node_t *)node)->exception); - } - if (((yp_rescue_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_rescue_node_t *)node)->statements); - } - if (((yp_rescue_node_t *)node)->consequent != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_rescue_node_t *)node)->consequent); - } - break; - case YP_NODE_REST_PARAMETER_NODE: - break; - case YP_NODE_RETRY_NODE: - break; - case YP_NODE_RETURN_NODE: - if (((yp_return_node_t *)node)->arguments != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_return_node_t *)node)->arguments); - } - break; - case YP_NODE_SELF_NODE: - break; - case YP_NODE_SINGLETON_CLASS_NODE: - yp_token_list_free(&((yp_singleton_class_node_t *)node)->locals); - yp_node_destroy(parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->expression); - if (((yp_singleton_class_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->statements); - } - break; - case YP_NODE_SOURCE_ENCODING_NODE: - break; - case YP_NODE_SOURCE_FILE_NODE: - yp_string_free(&((yp_source_file_node_t *)node)->filepath); - break; - case YP_NODE_SOURCE_LINE_NODE: - break; - case YP_NODE_SPLAT_NODE: - if (((yp_splat_node_t *)node)->expression != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_splat_node_t *)node)->expression); - } - break; - case YP_NODE_STATEMENTS_NODE: - yp_node_list_free(parser, &((yp_statements_node_t *)node)->body); - break; - case YP_NODE_STRING_CONCAT_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_string_concat_node_t *)node)->left); - yp_node_destroy(parser, (yp_node_t *)((yp_string_concat_node_t *)node)->right); - break; - case YP_NODE_STRING_INTERPOLATED_NODE: - if (((yp_string_interpolated_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_string_interpolated_node_t *)node)->statements); - } - break; - case YP_NODE_STRING_NODE: - yp_string_free(&((yp_string_node_t *)node)->unescaped); - break; - case YP_NODE_SUPER_NODE: - if (((yp_super_node_t *)node)->arguments != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_super_node_t *)node)->arguments); - } - if (((yp_super_node_t *)node)->block != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_super_node_t *)node)->block); - } - break; - case YP_NODE_SYMBOL_NODE: - yp_string_free(&((yp_symbol_node_t *)node)->unescaped); - break; - case YP_NODE_TRUE_NODE: - break; - case YP_NODE_UNDEF_NODE: - yp_node_list_free(parser, &((yp_undef_node_t *)node)->names); - break; - case YP_NODE_UNLESS_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_unless_node_t *)node)->predicate); - if (((yp_unless_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_unless_node_t *)node)->statements); - } - if (((yp_unless_node_t *)node)->consequent != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_unless_node_t *)node)->consequent); - } - break; - case YP_NODE_UNTIL_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_until_node_t *)node)->predicate); - if (((yp_until_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_until_node_t *)node)->statements); - } - break; - case YP_NODE_WHEN_NODE: - yp_node_list_free(parser, &((yp_when_node_t *)node)->conditions); - if (((yp_when_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_when_node_t *)node)->statements); - } - break; - case YP_NODE_WHILE_NODE: - yp_node_destroy(parser, (yp_node_t *)((yp_while_node_t *)node)->predicate); - if (((yp_while_node_t *)node)->statements != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_while_node_t *)node)->statements); - } - break; - case YP_NODE_X_STRING_NODE: - yp_string_free(&((yp_x_string_node_t *)node)->unescaped); - break; - case YP_NODE_YIELD_NODE: - if (((yp_yield_node_t *)node)->arguments != NULL) { - yp_node_destroy(parser, (yp_node_t *)((yp_yield_node_t *)node)->arguments); - } - break; - default: - assert(false && "unreachable"); - break; - } - free(node); -} - -static void -yp_node_memsize_node(yp_node_t *node, yp_memsize_t *memsize) { - memsize->node_count++; - - switch (node->type) { - case YP_NODE_ALIAS_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_alias_node_t *)node)->new_name, memsize); - yp_node_memsize_node((yp_node_t *)((yp_alias_node_t *)node)->old_name, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_ALTERNATION_PATTERN_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_alternation_pattern_node_t *)node)->left, memsize); - yp_node_memsize_node((yp_node_t *)((yp_alternation_pattern_node_t *)node)->right, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_AND_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_and_node_t *)node)->left, memsize); - yp_node_memsize_node((yp_node_t *)((yp_and_node_t *)node)->right, memsize); - break; - } - case YP_NODE_ARGUMENTS_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_arguments_node_t *)node)->arguments, memsize); - break; - } - case YP_NODE_ARRAY_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_array_node_t *)node)->elements, memsize); - break; - } - case YP_NODE_ARRAY_PATTERN_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_array_pattern_node_t *)node)->constant != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_array_pattern_node_t *)node)->constant, memsize); - } - yp_node_list_memsize(&((yp_array_pattern_node_t *)node)->requireds, memsize); - if (((yp_array_pattern_node_t *)node)->rest != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_array_pattern_node_t *)node)->rest, memsize); - } - yp_node_list_memsize(&((yp_array_pattern_node_t *)node)->posts, memsize); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_ASSOC_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_assoc_node_t *)node)->key, memsize); - if (((yp_assoc_node_t *)node)->value != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_assoc_node_t *)node)->value, memsize); - } - break; - } - case YP_NODE_ASSOC_SPLAT_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_assoc_splat_node_t *)node)->value != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_assoc_splat_node_t *)node)->value, memsize); - } - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_BEGIN_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_begin_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_begin_node_t *)node)->statements, memsize); - } - if (((yp_begin_node_t *)node)->rescue_clause != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_begin_node_t *)node)->rescue_clause, memsize); - } - if (((yp_begin_node_t *)node)->else_clause != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_begin_node_t *)node)->else_clause, memsize); - } - if (((yp_begin_node_t *)node)->ensure_clause != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_begin_node_t *)node)->ensure_clause, memsize); - } - break; - } - case YP_NODE_BLOCK_ARGUMENT_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_block_argument_node_t *)node)->expression != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_block_argument_node_t *)node)->expression, memsize); - } - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_BLOCK_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_token_list_memsize(&((yp_block_node_t *)node)->locals); - if (((yp_block_node_t *)node)->parameters != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_block_node_t *)node)->parameters, memsize); - } - if (((yp_block_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_block_node_t *)node)->statements, memsize); - } - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_BLOCK_PARAMETER_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_BLOCK_PARAMETERS_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_block_parameters_node_t *)node)->parameters != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_block_parameters_node_t *)node)->parameters, memsize); - } - memsize->memsize += yp_token_list_memsize(&((yp_block_parameters_node_t *)node)->locals); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_BREAK_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_break_node_t *)node)->arguments != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_break_node_t *)node)->arguments, memsize); - } - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_CALL_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_call_node_t *)node)->receiver != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_call_node_t *)node)->receiver, memsize); - } - if (((yp_call_node_t *)node)->arguments != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_call_node_t *)node)->arguments, memsize); - } - if (((yp_call_node_t *)node)->block != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_call_node_t *)node)->block, memsize); - } - memsize->memsize += yp_string_memsize(&((yp_call_node_t *)node)->name); - break; - } - case YP_NODE_CAPTURE_PATTERN_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_capture_pattern_node_t *)node)->value, memsize); - yp_node_memsize_node((yp_node_t *)((yp_capture_pattern_node_t *)node)->target, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_CASE_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_case_node_t *)node)->predicate != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_case_node_t *)node)->predicate, memsize); - } - yp_node_list_memsize(&((yp_case_node_t *)node)->conditions, memsize); - if (((yp_case_node_t *)node)->consequent != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_case_node_t *)node)->consequent, memsize); - } - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_CLASS_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_token_list_memsize(&((yp_class_node_t *)node)->locals); - yp_node_memsize_node((yp_node_t *)((yp_class_node_t *)node)->constant_path, memsize); - if (((yp_class_node_t *)node)->superclass != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_class_node_t *)node)->superclass, memsize); - } - if (((yp_class_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_class_node_t *)node)->statements, memsize); - } - break; - } - case YP_NODE_CLASS_VARIABLE_READ_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_CLASS_VARIABLE_WRITE_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += sizeof(yp_location_t); - if (((yp_class_variable_write_node_t *)node)->value != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_class_variable_write_node_t *)node)->value, memsize); - } - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_CONSTANT_PATH_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_constant_path_node_t *)node)->parent != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_constant_path_node_t *)node)->parent, memsize); - } - yp_node_memsize_node((yp_node_t *)((yp_constant_path_node_t *)node)->child, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_CONSTANT_PATH_WRITE_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_constant_path_write_node_t *)node)->target, memsize); - if (((yp_constant_path_write_node_t *)node)->value != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_constant_path_write_node_t *)node)->value, memsize); - } - break; - } - case YP_NODE_CONSTANT_READ_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_DEF_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_def_node_t *)node)->receiver != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_def_node_t *)node)->receiver, memsize); - } - if (((yp_def_node_t *)node)->parameters != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_def_node_t *)node)->parameters, memsize); - } - if (((yp_def_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_def_node_t *)node)->statements, memsize); - } - memsize->memsize += yp_token_list_memsize(&((yp_def_node_t *)node)->locals); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_DEFINED_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_defined_node_t *)node)->value, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_ELSE_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_else_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_else_node_t *)node)->statements, memsize); - } - break; - } - case YP_NODE_ENSURE_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_ensure_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_ensure_node_t *)node)->statements, memsize); - } - break; - } - case YP_NODE_FALSE_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_FIND_PATTERN_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_find_pattern_node_t *)node)->constant != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_find_pattern_node_t *)node)->constant, memsize); - } - yp_node_memsize_node((yp_node_t *)((yp_find_pattern_node_t *)node)->left, memsize); - yp_node_list_memsize(&((yp_find_pattern_node_t *)node)->requireds, memsize); - yp_node_memsize_node((yp_node_t *)((yp_find_pattern_node_t *)node)->right, memsize); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_FLOAT_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_FOR_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_for_node_t *)node)->index, memsize); - yp_node_memsize_node((yp_node_t *)((yp_for_node_t *)node)->collection, memsize); - if (((yp_for_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_for_node_t *)node)->statements, memsize); - } - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_FORWARDING_ARGUMENTS_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_FORWARDING_PARAMETER_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_FORWARDING_SUPER_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_forwarding_super_node_t *)node)->block != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_forwarding_super_node_t *)node)->block, memsize); - } - break; - } - case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_GLOBAL_VARIABLE_WRITE_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_global_variable_write_node_t *)node)->value != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_global_variable_write_node_t *)node)->value, memsize); - } - break; - } - case YP_NODE_HASH_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_hash_node_t *)node)->elements, memsize); - break; - } - case YP_NODE_HASH_PATTERN_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_hash_pattern_node_t *)node)->constant != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_hash_pattern_node_t *)node)->constant, memsize); - } - yp_node_list_memsize(&((yp_hash_pattern_node_t *)node)->assocs, memsize); - if (((yp_hash_pattern_node_t *)node)->kwrest != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_hash_pattern_node_t *)node)->kwrest, memsize); - } - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_IF_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_if_node_t *)node)->predicate, memsize); - if (((yp_if_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_if_node_t *)node)->statements, memsize); - } - if (((yp_if_node_t *)node)->consequent != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_if_node_t *)node)->consequent, memsize); - } - break; - } - case YP_NODE_IMAGINARY_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_imaginary_node_t *)node)->numeric, memsize); - break; - } - case YP_NODE_IN_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_in_node_t *)node)->pattern, memsize); - if (((yp_in_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_in_node_t *)node)->statements, memsize); - } - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_INSTANCE_VARIABLE_WRITE_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += sizeof(yp_location_t); - if (((yp_instance_variable_write_node_t *)node)->value != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_instance_variable_write_node_t *)node)->value, memsize); - } - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_INTEGER_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_interpolated_regular_expression_node_t *)node)->parts, memsize); - break; - } - case YP_NODE_INTERPOLATED_STRING_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_interpolated_string_node_t *)node)->parts, memsize); - break; - } - case YP_NODE_INTERPOLATED_SYMBOL_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_interpolated_symbol_node_t *)node)->parts, memsize); - break; - } - case YP_NODE_INTERPOLATED_X_STRING_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_interpolated_x_string_node_t *)node)->parts, memsize); - break; - } - case YP_NODE_KEYWORD_HASH_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_keyword_hash_node_t *)node)->elements, memsize); - break; - } - case YP_NODE_KEYWORD_PARAMETER_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_keyword_parameter_node_t *)node)->value != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_keyword_parameter_node_t *)node)->value, memsize); - } - break; - } - case YP_NODE_KEYWORD_REST_PARAMETER_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_LAMBDA_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_token_list_memsize(&((yp_lambda_node_t *)node)->locals); - if (((yp_lambda_node_t *)node)->parameters != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_lambda_node_t *)node)->parameters, memsize); - } - if (((yp_lambda_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_lambda_node_t *)node)->statements, memsize); - } - break; - } - case YP_NODE_LOCAL_VARIABLE_READ_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += sizeof(uint32_t); - break; - } - case YP_NODE_LOCAL_VARIABLE_WRITE_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += sizeof(yp_location_t); - if (((yp_local_variable_write_node_t *)node)->value != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_local_variable_write_node_t *)node)->value, memsize); - } - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(uint32_t); - break; - } - case YP_NODE_MATCH_PREDICATE_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_match_predicate_node_t *)node)->value, memsize); - yp_node_memsize_node((yp_node_t *)((yp_match_predicate_node_t *)node)->pattern, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_MATCH_REQUIRED_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_match_required_node_t *)node)->value, memsize); - yp_node_memsize_node((yp_node_t *)((yp_match_required_node_t *)node)->pattern, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_MISSING_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_MODULE_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_token_list_memsize(&((yp_module_node_t *)node)->locals); - yp_node_memsize_node((yp_node_t *)((yp_module_node_t *)node)->constant_path, memsize); - if (((yp_module_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_module_node_t *)node)->statements, memsize); - } - break; - } - case YP_NODE_MULTI_WRITE_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_multi_write_node_t *)node)->targets, memsize); - if (((yp_multi_write_node_t *)node)->value != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_multi_write_node_t *)node)->value, memsize); - } - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_NEXT_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_next_node_t *)node)->arguments != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_next_node_t *)node)->arguments, memsize); - } - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_NIL_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_NO_KEYWORDS_PARAMETER_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_OPERATOR_AND_ASSIGNMENT_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_operator_and_assignment_node_t *)node)->target, memsize); - yp_node_memsize_node((yp_node_t *)((yp_operator_and_assignment_node_t *)node)->value, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_OPERATOR_ASSIGNMENT_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_operator_assignment_node_t *)node)->target, memsize); - yp_node_memsize_node((yp_node_t *)((yp_operator_assignment_node_t *)node)->value, memsize); - break; - } - case YP_NODE_OPERATOR_OR_ASSIGNMENT_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_operator_or_assignment_node_t *)node)->target, memsize); - yp_node_memsize_node((yp_node_t *)((yp_operator_or_assignment_node_t *)node)->value, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_OPTIONAL_PARAMETER_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_optional_parameter_node_t *)node)->value, memsize); - break; - } - case YP_NODE_OR_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_or_node_t *)node)->left, memsize); - yp_node_memsize_node((yp_node_t *)((yp_or_node_t *)node)->right, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_PARAMETERS_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_parameters_node_t *)node)->requireds, memsize); - yp_node_list_memsize(&((yp_parameters_node_t *)node)->optionals, memsize); - yp_node_list_memsize(&((yp_parameters_node_t *)node)->posts, memsize); - if (((yp_parameters_node_t *)node)->rest != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_parameters_node_t *)node)->rest, memsize); - } - yp_node_list_memsize(&((yp_parameters_node_t *)node)->keywords, memsize); - if (((yp_parameters_node_t *)node)->keyword_rest != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_parameters_node_t *)node)->keyword_rest, memsize); - } - if (((yp_parameters_node_t *)node)->block != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_parameters_node_t *)node)->block, memsize); - } - break; - } - case YP_NODE_PARENTHESES_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_parentheses_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_parentheses_node_t *)node)->statements, memsize); - } - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_PINNED_EXPRESSION_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_pinned_expression_node_t *)node)->expression, memsize); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_PINNED_VARIABLE_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_pinned_variable_node_t *)node)->variable, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_POST_EXECUTION_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_post_execution_node_t *)node)->statements, memsize); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_PRE_EXECUTION_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_pre_execution_node_t *)node)->statements, memsize); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_PROGRAM_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_token_list_memsize(&((yp_program_node_t *)node)->locals); - yp_node_memsize_node((yp_node_t *)((yp_program_node_t *)node)->statements, memsize); - break; - } - case YP_NODE_RANGE_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_range_node_t *)node)->left != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_range_node_t *)node)->left, memsize); - } - if (((yp_range_node_t *)node)->right != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_range_node_t *)node)->right, memsize); - } - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_RATIONAL_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_rational_node_t *)node)->numeric, memsize); - break; - } - case YP_NODE_REDO_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_REGULAR_EXPRESSION_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_string_memsize(&((yp_regular_expression_node_t *)node)->unescaped); - break; - } - case YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_required_destructured_parameter_node_t *)node)->parameters, memsize); - break; - } - case YP_NODE_REQUIRED_PARAMETER_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_RESCUE_MODIFIER_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_rescue_modifier_node_t *)node)->expression, memsize); - yp_node_memsize_node((yp_node_t *)((yp_rescue_modifier_node_t *)node)->rescue_expression, memsize); - break; - } - case YP_NODE_RESCUE_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_rescue_node_t *)node)->exceptions, memsize); - if (((yp_rescue_node_t *)node)->exception != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_rescue_node_t *)node)->exception, memsize); - } - if (((yp_rescue_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_rescue_node_t *)node)->statements, memsize); - } - if (((yp_rescue_node_t *)node)->consequent != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_rescue_node_t *)node)->consequent, memsize); - } - break; - } - case YP_NODE_REST_PARAMETER_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_RETRY_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_RETURN_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_return_node_t *)node)->arguments != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_return_node_t *)node)->arguments, memsize); - } - break; - } - case YP_NODE_SELF_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_SINGLETON_CLASS_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_token_list_memsize(&((yp_singleton_class_node_t *)node)->locals); - yp_node_memsize_node((yp_node_t *)((yp_singleton_class_node_t *)node)->expression, memsize); - if (((yp_singleton_class_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_singleton_class_node_t *)node)->statements, memsize); - } - break; - } - case YP_NODE_SOURCE_ENCODING_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_SOURCE_FILE_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_string_memsize(&((yp_source_file_node_t *)node)->filepath); - break; - } - case YP_NODE_SOURCE_LINE_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_SPLAT_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_splat_node_t *)node)->expression != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_splat_node_t *)node)->expression, memsize); - } - break; - } - case YP_NODE_STATEMENTS_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_statements_node_t *)node)->body, memsize); - break; - } - case YP_NODE_STRING_CONCAT_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_string_concat_node_t *)node)->left, memsize); - yp_node_memsize_node((yp_node_t *)((yp_string_concat_node_t *)node)->right, memsize); - break; - } - case YP_NODE_STRING_INTERPOLATED_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_string_interpolated_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_string_interpolated_node_t *)node)->statements, memsize); - } - break; - } - case YP_NODE_STRING_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_string_memsize(&((yp_string_node_t *)node)->unescaped); - break; - } - case YP_NODE_SUPER_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_super_node_t *)node)->arguments != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_super_node_t *)node)->arguments, memsize); - } - if (((yp_super_node_t *)node)->block != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_super_node_t *)node)->block, memsize); - } - break; - } - case YP_NODE_SYMBOL_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_string_memsize(&((yp_symbol_node_t *)node)->unescaped); - break; - } - case YP_NODE_TRUE_NODE: { - memsize->memsize += sizeof(yp_node_t); - break; - } - case YP_NODE_UNDEF_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_undef_node_t *)node)->names, memsize); - memsize->memsize += sizeof(yp_location_t); - break; - } - case YP_NODE_UNLESS_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_unless_node_t *)node)->predicate, memsize); - if (((yp_unless_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_unless_node_t *)node)->statements, memsize); - } - if (((yp_unless_node_t *)node)->consequent != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_unless_node_t *)node)->consequent, memsize); - } - break; - } - case YP_NODE_UNTIL_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_until_node_t *)node)->predicate, memsize); - if (((yp_until_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_until_node_t *)node)->statements, memsize); - } - break; - } - case YP_NODE_WHEN_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_list_memsize(&((yp_when_node_t *)node)->conditions, memsize); - if (((yp_when_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_when_node_t *)node)->statements, memsize); - } - break; - } - case YP_NODE_WHILE_NODE: { - memsize->memsize += sizeof(yp_node_t); - yp_node_memsize_node((yp_node_t *)((yp_while_node_t *)node)->predicate, memsize); - if (((yp_while_node_t *)node)->statements != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_while_node_t *)node)->statements, memsize); - } - break; - } - case YP_NODE_X_STRING_NODE: { - memsize->memsize += sizeof(yp_node_t); - memsize->memsize += yp_string_memsize(&((yp_x_string_node_t *)node)->unescaped); - break; - } - case YP_NODE_YIELD_NODE: { - memsize->memsize += sizeof(yp_node_t); - if (((yp_yield_node_t *)node)->arguments != NULL) { - yp_node_memsize_node((yp_node_t *)((yp_yield_node_t *)node)->arguments, memsize); - } - break; - } - } -} - -// Calculates the memory footprint of a given node. -__attribute__((__visibility__("default"))) extern void -yp_node_memsize(yp_node_t *node, yp_memsize_t *memsize) { - *memsize = (yp_memsize_t) { .memsize = 0, .node_count = 0 }; - yp_node_memsize_node(node, memsize); -} diff --git a/src/main/c/yarp/src/yarp/src/pack.c b/src/main/c/yarp/src/yarp/src/pack.c deleted file mode 100644 index c9c1aa6ea8d8..000000000000 --- a/src/main/c/yarp/src/yarp/src/pack.c +++ /dev/null @@ -1,493 +0,0 @@ -#include "yarp/pack.h" - -#include -#include - -static uintmax_t -strtoumaxc(const char **format); - -__attribute__((__visibility__("default"))) extern yp_pack_result -yp_pack_parse(__attribute__((unused)) yp_pack_version version, yp_pack_variant variant, const char **format, const char *format_end, - yp_pack_type *type, yp_pack_signed *signed_type, yp_pack_endian *endian, yp_pack_size *size, - yp_pack_length_type *length_type, uint64_t *length, yp_pack_encoding *encoding) { - - if (*encoding == YP_PACK_ENCODING_START) { - *encoding = YP_PACK_ENCODING_US_ASCII; - } - - if (*format == format_end) { - *type = YP_PACK_END; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - *length_type = YP_PACK_LENGTH_NA; - return YP_PACK_OK; - } - - *length_type = YP_PACK_LENGTH_FIXED; - *length = 1; - bool length_changed_allowed = true; - - char directive = **format; - (*format)++; - switch (directive) { - case ' ': - case '\t': - case '\n': - case '\v': - case '\f': - case '\r': - *type = YP_PACK_SPACE; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - *length_type = YP_PACK_LENGTH_NA; - *length = 0; - return YP_PACK_OK; - case '#': - while ((*format < format_end) && (**format != '\n')) { - (*format)++; - } - *type = YP_PACK_COMMENT; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - *length_type = YP_PACK_LENGTH_NA; - *length = 0; - return YP_PACK_OK; - case 'C': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_UNSIGNED; - *endian = YP_PACK_AGNOSTIC_ENDIAN; - *size = YP_PACK_SIZE_8; - break; - case 'S': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_UNSIGNED; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_16; - break; - case 'L': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_UNSIGNED; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_32; - break; - case 'Q': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_UNSIGNED; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_64; - break; - case 'J': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_UNSIGNED; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_P; - break; - case 'c': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_SIGNED; - *endian = YP_PACK_AGNOSTIC_ENDIAN; - *size = YP_PACK_SIZE_8; - break; - case 's': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_SIGNED; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_16; - break; - case 'l': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_SIGNED; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_32; - break; - case 'q': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_SIGNED; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_64; - break; - case 'j': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_SIGNED; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_P; - break; - case 'I': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_UNSIGNED; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_INT; - break; - case 'i': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_SIGNED; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_INT; - break; - case 'n': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_UNSIGNED; - *endian = YP_PACK_BIG_ENDIAN; - *size = YP_PACK_SIZE_16; - length_changed_allowed = false; - break; - case 'N': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_UNSIGNED; - *endian = YP_PACK_BIG_ENDIAN; - *size = YP_PACK_SIZE_32; - length_changed_allowed = false; - break; - case 'v': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_UNSIGNED; - *endian = YP_PACK_LITTLE_ENDIAN; - *size = YP_PACK_SIZE_16; - length_changed_allowed = false; - break; - case 'V': - *type = YP_PACK_INTEGER; - *signed_type = YP_PACK_UNSIGNED; - *endian = YP_PACK_LITTLE_ENDIAN; - *size = YP_PACK_SIZE_32; - length_changed_allowed = false; - break; - case 'U': - *type = YP_PACK_UTF8; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'w': - *type = YP_PACK_BER; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'D': - case 'd': - *type = YP_PACK_FLOAT; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_64; - break; - case 'F': - case 'f': - *type = YP_PACK_FLOAT; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_NATIVE_ENDIAN; - *size = YP_PACK_SIZE_32; - break; - case 'E': - *type = YP_PACK_FLOAT; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_LITTLE_ENDIAN; - *size = YP_PACK_SIZE_64; - break; - case 'e': - *type = YP_PACK_FLOAT; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_LITTLE_ENDIAN; - *size = YP_PACK_SIZE_32; - break; - case 'G': - *type = YP_PACK_FLOAT; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_BIG_ENDIAN; - *size = YP_PACK_SIZE_64; - break; - case 'g': - *type = YP_PACK_FLOAT; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_BIG_ENDIAN; - *size = YP_PACK_SIZE_32; - break; - case 'A': - *type = YP_PACK_STRING_SPACE_PADDED; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'a': - *type = YP_PACK_STRING_NULL_PADDED; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'Z': - *type = YP_PACK_STRING_NULL_TERMINATED; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'B': - *type = YP_PACK_STRING_MSB; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'b': - *type = YP_PACK_STRING_LSB; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'H': - *type = YP_PACK_STRING_HEX_HIGH; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'h': - *type = YP_PACK_STRING_HEX_LOW; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'u': - *type = YP_PACK_STRING_UU; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'M': - *type = YP_PACK_STRING_MIME; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'm': - *type = YP_PACK_STRING_BASE64; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'P': - *type = YP_PACK_STRING_FIXED; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'p': - *type = YP_PACK_STRING_POINTER; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case '@': - *type = YP_PACK_MOVE; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'X': - *type = YP_PACK_BACK; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case 'x': - *type = YP_PACK_NULL; - *signed_type = YP_PACK_SIGNED_NA; - *endian = YP_PACK_ENDIAN_NA; - *size = YP_PACK_SIZE_NA; - break; - case '%': - return YP_PACK_ERROR_UNSUPPORTED_DIRECTIVE; - default: - return YP_PACK_ERROR_UNKNOWN_DIRECTIVE; - } - - bool explicit_endian = false; - - while (*format < format_end) { - switch (**format) { - case '_': - case '!': - (*format)++; - if (*type != YP_PACK_INTEGER || !length_changed_allowed) { - return YP_PACK_ERROR_BANG_NOT_ALLOWED; - } - switch (*size) { - case YP_PACK_SIZE_SHORT: - case YP_PACK_SIZE_INT: - case YP_PACK_SIZE_LONG: - case YP_PACK_SIZE_LONG_LONG: - break; - case YP_PACK_SIZE_16: - *size = YP_PACK_SIZE_SHORT; - break; - case YP_PACK_SIZE_32: - *size = YP_PACK_SIZE_LONG; - break; - case YP_PACK_SIZE_64: - *size = YP_PACK_SIZE_LONG_LONG; - break; - case YP_PACK_SIZE_P: - break; - default: - return YP_PACK_ERROR_BANG_NOT_ALLOWED; - } - break; - case '<': - (*format)++; - if (explicit_endian) { - return YP_PACK_ERROR_DOUBLE_ENDIAN; - } - *endian = YP_PACK_LITTLE_ENDIAN; - explicit_endian = true; - break; - case '>': - (*format)++; - if (explicit_endian) { - return YP_PACK_ERROR_DOUBLE_ENDIAN; - } - *endian = YP_PACK_BIG_ENDIAN; - explicit_endian = true; - break; - default: - goto exit_modifier_loop; - } - } - -exit_modifier_loop: - - if (variant == YP_PACK_VARIANT_UNPACK && *type == YP_PACK_MOVE) { - *length = 0; - } - - if (*format < format_end) { - if (**format == '*') { - switch (*type) { - case YP_PACK_NULL: - case YP_PACK_BACK: - switch (variant) { - case YP_PACK_VARIANT_PACK: - *length_type = YP_PACK_LENGTH_FIXED; - break; - case YP_PACK_VARIANT_UNPACK: - *length_type = YP_PACK_LENGTH_MAX; - break; - } - *length = 0; - break; - - case YP_PACK_MOVE: - switch (variant) { - case YP_PACK_VARIANT_PACK: - *length_type = YP_PACK_LENGTH_FIXED; - break; - case YP_PACK_VARIANT_UNPACK: - *length_type = YP_PACK_LENGTH_RELATIVE; - break; - } - *length = 0; - break; - - case YP_PACK_STRING_UU: - *length_type = YP_PACK_LENGTH_FIXED; - *length = 0; - break; - - case YP_PACK_STRING_FIXED: - switch (variant) { - case YP_PACK_VARIANT_PACK: - *length_type = YP_PACK_LENGTH_FIXED; - *length = 1; - break; - case YP_PACK_VARIANT_UNPACK: - *length_type = YP_PACK_LENGTH_MAX; - *length = 0; - break; - } - break; - - case YP_PACK_STRING_MIME: - case YP_PACK_STRING_BASE64: - *length_type = YP_PACK_LENGTH_FIXED; - *length = 1; - break; - - default: - *length_type = YP_PACK_LENGTH_MAX; - *length = 0; - break; - } - - (*format)++; - } else if (**format >= '0' && **format <= '9') { - errno = 0; - *length_type = YP_PACK_LENGTH_FIXED; - #if UINTMAX_MAX < UINT64_MAX - #error "YARP's design assumes uintmax_t is at least as large as uint64_t" - #endif - uintmax_t length_max = strtoumaxc(format); - if (errno || length_max > UINT64_MAX) { - return YP_PACK_ERROR_LENGTH_TOO_BIG; - } - *length = (uint64_t) length_max; - } - } - - switch (*type) { - case YP_PACK_UTF8: - /* if encoding is US-ASCII, upgrade to UTF-8 */ - if (*encoding == YP_PACK_ENCODING_US_ASCII) { - *encoding = YP_PACK_ENCODING_UTF_8; - } - break; - case YP_PACK_STRING_MIME: - case YP_PACK_STRING_BASE64: - case YP_PACK_STRING_UU: - /* keep US-ASCII (do nothing) */ - break; - default: - /* fall back to BINARY */ - *encoding = YP_PACK_ENCODING_ASCII_8BIT; - break; - } - - return YP_PACK_OK; -} - -__attribute__((__visibility__("default"))) extern size_t -yp_size_to_native(yp_pack_size size) { - switch (size) { - case YP_PACK_SIZE_SHORT: - return sizeof(short); - case YP_PACK_SIZE_INT: - return sizeof(int); - case YP_PACK_SIZE_LONG: - return sizeof(long); - case YP_PACK_SIZE_LONG_LONG: - return sizeof(long long); - case YP_PACK_SIZE_8: - return 1; - case YP_PACK_SIZE_16: - return 2; - case YP_PACK_SIZE_32: - return 4; - case YP_PACK_SIZE_64: - return 8; - case YP_PACK_SIZE_P: - return sizeof(void *); - default: - return 0; - } -} - -static uintmax_t -strtoumaxc(const char **format) { - uintmax_t value = 0; - while (**format >= '0' && **format <= '9') { - if (value > UINTMAX_MAX / 10) { - errno = ERANGE; - } - value = value * 10 + ((uintmax_t) (**format - '0')); - (*format)++; - } - return value; -} diff --git a/src/main/c/yarp/src/yarp/src/prettyprint.c b/src/main/c/yarp/src/yarp/src/prettyprint.c deleted file mode 100644 index f99f7453776b..000000000000 --- a/src/main/c/yarp/src/yarp/src/prettyprint.c +++ /dev/null @@ -1,1463 +0,0 @@ -/******************************************************************************/ -/* This file is generated by the bin/template script and should not be */ -/* modified manually. See */ -/* bin/templates/src/prettyprint.c.erb */ -/* if you are looking to modify the */ -/* template */ -/******************************************************************************/ -#include - -#include "yarp/ast.h" -#include "yarp/parser.h" -#include "yarp/util/yp_buffer.h" - -static void -prettyprint_token(yp_buffer_t *buffer, yp_token_t *token) { - yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, token->start, (size_t) (token->end - token->start)); - yp_buffer_append_str(buffer, "\"", 1); -} - -static void -prettyprint_location(yp_buffer_t *buffer, yp_parser_t *parser, yp_location_t *location) { - char printed[] = "[0000-0000]"; - sprintf(printed, "[%04ld-%04ld]", location->start - parser->start, location->end - parser->start); - yp_buffer_append_str(buffer, printed, strlen(printed)); -} - -static void -prettyprint_node(yp_buffer_t *buffer, yp_parser_t *parser, yp_node_t *node) { - switch (node->type) { - case YP_NODE_ALIAS_NODE: { - yp_buffer_append_str(buffer, "AliasNode(", 10); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_alias_node_t *)node)->new_name); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_alias_node_t *)node)->old_name); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_alias_node_t *)node)->keyword_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_ALTERNATION_PATTERN_NODE: { - yp_buffer_append_str(buffer, "AlternationPatternNode(", 23); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->left); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->right); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_alternation_pattern_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_AND_NODE: { - yp_buffer_append_str(buffer, "AndNode(", 8); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_and_node_t *)node)->left); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_and_node_t *)node)->right); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_and_node_t *)node)->operator); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_ARGUMENTS_NODE: { - yp_buffer_append_str(buffer, "ArgumentsNode(", 14); - for (uint32_t index = 0; index < ((yp_arguments_node_t *)node)->arguments.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_arguments_node_t *) node)->arguments.nodes[index]); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_ARRAY_NODE: { - yp_buffer_append_str(buffer, "ArrayNode(", 10); - for (uint32_t index = 0; index < ((yp_array_node_t *)node)->elements.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_array_node_t *) node)->elements.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_array_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_array_node_t *)node)->opening); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_array_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_array_node_t *)node)->closing); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_ARRAY_PATTERN_NODE: { - yp_buffer_append_str(buffer, "ArrayPatternNode(", 17); - if (((yp_array_pattern_node_t *)node)->constant == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->constant); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_array_pattern_node_t *)node)->requireds.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_array_pattern_node_t *) node)->requireds.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_array_pattern_node_t *)node)->rest == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->rest); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_array_pattern_node_t *)node)->posts.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_array_pattern_node_t *) node)->posts.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_array_pattern_node_t *)node)->opening_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_array_pattern_node_t *)node)->opening_loc); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_array_pattern_node_t *)node)->closing_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_array_pattern_node_t *)node)->closing_loc); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_ASSOC_NODE: { - yp_buffer_append_str(buffer, "AssocNode(", 10); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_assoc_node_t *)node)->key); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_assoc_node_t *)node)->value == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_assoc_node_t *)node)->value); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_assoc_node_t *)node)->operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_assoc_node_t *)node)->operator); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_ASSOC_SPLAT_NODE: { - yp_buffer_append_str(buffer, "AssocSplatNode(", 15); - if (((yp_assoc_splat_node_t *)node)->value == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_assoc_splat_node_t *)node)->value); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_assoc_splat_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_BEGIN_NODE: { - yp_buffer_append_str(buffer, "BeginNode(", 10); - if (((yp_begin_node_t *)node)->begin_keyword.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_begin_node_t *)node)->begin_keyword); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_begin_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_begin_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_begin_node_t *)node)->rescue_clause == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_begin_node_t *)node)->rescue_clause); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_begin_node_t *)node)->else_clause == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_begin_node_t *)node)->else_clause); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_begin_node_t *)node)->ensure_clause == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_begin_node_t *)node)->ensure_clause); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_begin_node_t *)node)->end_keyword.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_begin_node_t *)node)->end_keyword); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_BLOCK_ARGUMENT_NODE: { - yp_buffer_append_str(buffer, "BlockArgumentNode(", 18); - if (((yp_block_argument_node_t *)node)->expression == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_block_argument_node_t *)node)->expression); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_block_argument_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_BLOCK_NODE: { - yp_buffer_append_str(buffer, "BlockNode(", 10); - for (uint32_t index = 0; index < ((yp_block_node_t *)node)->locals.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_token(buffer, &((yp_block_node_t *)node)->locals.tokens[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_block_node_t *)node)->parameters == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_block_node_t *)node)->parameters); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_block_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_block_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_block_node_t *)node)->opening_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_block_node_t *)node)->closing_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_BLOCK_PARAMETER_NODE: { - yp_buffer_append_str(buffer, "BlockParameterNode(", 19); - if (((yp_block_parameter_node_t *)node)->name.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_block_parameter_node_t *)node)->name); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_block_parameter_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_BLOCK_PARAMETERS_NODE: { - yp_buffer_append_str(buffer, "BlockParametersNode(", 20); - if (((yp_block_parameters_node_t *)node)->parameters == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_block_parameters_node_t *)node)->parameters); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_block_parameters_node_t *)node)->locals.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_token(buffer, &((yp_block_parameters_node_t *)node)->locals.tokens[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_block_parameters_node_t *)node)->opening_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_block_parameters_node_t *)node)->opening_loc); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_block_parameters_node_t *)node)->closing_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_block_parameters_node_t *)node)->closing_loc); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_BREAK_NODE: { - yp_buffer_append_str(buffer, "BreakNode(", 10); - if (((yp_break_node_t *)node)->arguments == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_break_node_t *)node)->arguments); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_break_node_t *)node)->keyword_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_CALL_NODE: { - yp_buffer_append_str(buffer, "CallNode(", 9); - if (((yp_call_node_t *)node)->receiver == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_node_t *)node)->receiver); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->call_operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_call_node_t *)node)->call_operator); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->message.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_call_node_t *)node)->message); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_call_node_t *)node)->opening); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->arguments == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_node_t *)node)->arguments); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_call_node_t *)node)->closing); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_call_node_t *)node)->block == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_call_node_t *)node)->block); - } - yp_buffer_append_str(buffer, ", ", 2); yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, yp_string_source(&((yp_call_node_t *)node)->name), yp_string_length(&((yp_call_node_t *)node)->name)); - yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_CAPTURE_PATTERN_NODE: { - yp_buffer_append_str(buffer, "CapturePatternNode(", 19); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->value); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->target); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_capture_pattern_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_CASE_NODE: { - yp_buffer_append_str(buffer, "CaseNode(", 9); - if (((yp_case_node_t *)node)->predicate == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_case_node_t *)node)->predicate); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_case_node_t *)node)->conditions.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_case_node_t *) node)->conditions.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_case_node_t *)node)->consequent == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_case_node_t *)node)->consequent); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_case_node_t *)node)->case_keyword_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_case_node_t *)node)->end_keyword_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_CLASS_NODE: { - yp_buffer_append_str(buffer, "ClassNode(", 10); - for (uint32_t index = 0; index < ((yp_class_node_t *)node)->locals.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_token(buffer, &((yp_class_node_t *)node)->locals.tokens[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_class_node_t *)node)->class_keyword); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_node_t *)node)->constant_path); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_class_node_t *)node)->inheritance_operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_class_node_t *)node)->inheritance_operator); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_class_node_t *)node)->superclass == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_node_t *)node)->superclass); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_class_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_class_node_t *)node)->end_keyword); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_CLASS_VARIABLE_READ_NODE: { - yp_buffer_append_str(buffer, "ClassVariableReadNode(", 22); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_CLASS_VARIABLE_WRITE_NODE: { - yp_buffer_append_str(buffer, "ClassVariableWriteNode(", 23); - prettyprint_location(buffer, parser, &((yp_class_variable_write_node_t *)node)->name_loc); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_class_variable_write_node_t *)node)->value == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_class_variable_write_node_t *)node)->value); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_class_variable_write_node_t *)node)->operator_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_class_variable_write_node_t *)node)->operator_loc); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_CONSTANT_PATH_NODE: { - yp_buffer_append_str(buffer, "ConstantPathNode(", 17); - if (((yp_constant_path_node_t *)node)->parent == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_node_t *)node)->parent); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_node_t *)node)->child); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_constant_path_node_t *)node)->delimiter_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_CONSTANT_PATH_WRITE_NODE: { - yp_buffer_append_str(buffer, "ConstantPathWriteNode(", 22); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->target); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_constant_path_write_node_t *)node)->operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_constant_path_write_node_t *)node)->operator); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_constant_path_write_node_t *)node)->value == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->value); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_CONSTANT_READ_NODE: { - yp_buffer_append_str(buffer, "ConstantReadNode(", 17); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_DEF_NODE: { - yp_buffer_append_str(buffer, "DefNode(", 8); - prettyprint_token(buffer, &((yp_def_node_t *)node)->name); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->receiver == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_def_node_t *)node)->receiver); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->parameters == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_def_node_t *)node)->parameters); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_def_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_def_node_t *)node)->locals.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_token(buffer, &((yp_def_node_t *)node)->locals.tokens[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->def_keyword_loc); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->operator_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->operator_loc); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->lparen_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->lparen_loc); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->rparen_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->rparen_loc); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->equal_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->equal_loc); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_def_node_t *)node)->end_keyword_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_def_node_t *)node)->end_keyword_loc); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_DEFINED_NODE: { - yp_buffer_append_str(buffer, "DefinedNode(", 12); - if (((yp_defined_node_t *)node)->lparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_defined_node_t *)node)->lparen); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_defined_node_t *)node)->value); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_defined_node_t *)node)->rparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_defined_node_t *)node)->rparen); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_defined_node_t *)node)->keyword_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_ELSE_NODE: { - yp_buffer_append_str(buffer, "ElseNode(", 9); - prettyprint_token(buffer, &((yp_else_node_t *)node)->else_keyword); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_else_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_else_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_else_node_t *)node)->end_keyword.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_else_node_t *)node)->end_keyword); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_ENSURE_NODE: { - yp_buffer_append_str(buffer, "EnsureNode(", 11); - prettyprint_token(buffer, &((yp_ensure_node_t *)node)->ensure_keyword); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_ensure_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_ensure_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_ensure_node_t *)node)->end_keyword); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_FALSE_NODE: { - yp_buffer_append_str(buffer, "FalseNode(", 10); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_FIND_PATTERN_NODE: { - yp_buffer_append_str(buffer, "FindPatternNode(", 16); - if (((yp_find_pattern_node_t *)node)->constant == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->constant); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->left); - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_find_pattern_node_t *)node)->requireds.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_find_pattern_node_t *) node)->requireds.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->right); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_find_pattern_node_t *)node)->opening_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_find_pattern_node_t *)node)->opening_loc); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_find_pattern_node_t *)node)->closing_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_find_pattern_node_t *)node)->closing_loc); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_FLOAT_NODE: { - yp_buffer_append_str(buffer, "FloatNode(", 10); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_FOR_NODE: { - yp_buffer_append_str(buffer, "ForNode(", 8); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_for_node_t *)node)->index); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_for_node_t *)node)->collection); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_for_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_for_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_for_node_t *)node)->for_keyword_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_for_node_t *)node)->in_keyword_loc); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_for_node_t *)node)->do_keyword_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_for_node_t *)node)->do_keyword_loc); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_for_node_t *)node)->end_keyword_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_FORWARDING_ARGUMENTS_NODE: { - yp_buffer_append_str(buffer, "ForwardingArgumentsNode(", 24); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_FORWARDING_PARAMETER_NODE: { - yp_buffer_append_str(buffer, "ForwardingParameterNode(", 24); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_FORWARDING_SUPER_NODE: { - yp_buffer_append_str(buffer, "ForwardingSuperNode(", 20); - if (((yp_forwarding_super_node_t *)node)->block == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_forwarding_super_node_t *)node)->block); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { - yp_buffer_append_str(buffer, "GlobalVariableReadNode(", 23); - prettyprint_token(buffer, &((yp_global_variable_read_node_t *)node)->name); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_GLOBAL_VARIABLE_WRITE_NODE: { - yp_buffer_append_str(buffer, "GlobalVariableWriteNode(", 24); - prettyprint_token(buffer, &((yp_global_variable_write_node_t *)node)->name); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_global_variable_write_node_t *)node)->operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_global_variable_write_node_t *)node)->operator); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_global_variable_write_node_t *)node)->value == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_global_variable_write_node_t *)node)->value); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_HASH_NODE: { - yp_buffer_append_str(buffer, "HashNode(", 9); - prettyprint_token(buffer, &((yp_hash_node_t *)node)->opening); - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_hash_node_t *)node)->elements.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_hash_node_t *) node)->elements.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_hash_node_t *)node)->closing); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_HASH_PATTERN_NODE: { - yp_buffer_append_str(buffer, "HashPatternNode(", 16); - if (((yp_hash_pattern_node_t *)node)->constant == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->constant); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_hash_pattern_node_t *)node)->assocs.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_hash_pattern_node_t *) node)->assocs.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_hash_pattern_node_t *)node)->kwrest == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->kwrest); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_hash_pattern_node_t *)node)->opening_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_hash_pattern_node_t *)node)->opening_loc); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_hash_pattern_node_t *)node)->closing_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_hash_pattern_node_t *)node)->closing_loc); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_IF_NODE: { - yp_buffer_append_str(buffer, "IfNode(", 7); - prettyprint_token(buffer, &((yp_if_node_t *)node)->if_keyword); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_if_node_t *)node)->predicate); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_if_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_if_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_if_node_t *)node)->consequent == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_if_node_t *)node)->consequent); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_if_node_t *)node)->end_keyword.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_if_node_t *)node)->end_keyword); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_IMAGINARY_NODE: { - yp_buffer_append_str(buffer, "ImaginaryNode(", 14); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_imaginary_node_t *)node)->numeric); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_IN_NODE: { - yp_buffer_append_str(buffer, "InNode(", 7); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_in_node_t *)node)->pattern); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_in_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_in_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_in_node_t *)node)->in_loc); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_in_node_t *)node)->then_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_in_node_t *)node)->then_loc); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { - yp_buffer_append_str(buffer, "InstanceVariableReadNode(", 25); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_INSTANCE_VARIABLE_WRITE_NODE: { - yp_buffer_append_str(buffer, "InstanceVariableWriteNode(", 26); - prettyprint_location(buffer, parser, &((yp_instance_variable_write_node_t *)node)->name_loc); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_instance_variable_write_node_t *)node)->value == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_instance_variable_write_node_t *)node)->value); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_instance_variable_write_node_t *)node)->operator_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_instance_variable_write_node_t *)node)->operator_loc); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_INTEGER_NODE: { - yp_buffer_append_str(buffer, "IntegerNode(", 12); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE: { - yp_buffer_append_str(buffer, "InterpolatedRegularExpressionNode(", 34); - prettyprint_token(buffer, &((yp_interpolated_regular_expression_node_t *)node)->opening); - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_interpolated_regular_expression_node_t *)node)->parts.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_interpolated_regular_expression_node_t *) node)->parts.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_interpolated_regular_expression_node_t *)node)->closing); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_INTERPOLATED_STRING_NODE: { - yp_buffer_append_str(buffer, "InterpolatedStringNode(", 23); - if (((yp_interpolated_string_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_interpolated_string_node_t *)node)->opening); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_interpolated_string_node_t *)node)->parts.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_interpolated_string_node_t *) node)->parts.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_interpolated_string_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_interpolated_string_node_t *)node)->closing); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_INTERPOLATED_SYMBOL_NODE: { - yp_buffer_append_str(buffer, "InterpolatedSymbolNode(", 23); - if (((yp_interpolated_symbol_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_interpolated_symbol_node_t *)node)->opening); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_interpolated_symbol_node_t *)node)->parts.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_interpolated_symbol_node_t *) node)->parts.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_interpolated_symbol_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_interpolated_symbol_node_t *)node)->closing); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_INTERPOLATED_X_STRING_NODE: { - yp_buffer_append_str(buffer, "InterpolatedXStringNode(", 24); - prettyprint_token(buffer, &((yp_interpolated_x_string_node_t *)node)->opening); - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_interpolated_x_string_node_t *)node)->parts.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_interpolated_x_string_node_t *) node)->parts.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_interpolated_x_string_node_t *)node)->closing); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_KEYWORD_HASH_NODE: { - yp_buffer_append_str(buffer, "KeywordHashNode(", 16); - for (uint32_t index = 0; index < ((yp_keyword_hash_node_t *)node)->elements.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_keyword_hash_node_t *) node)->elements.nodes[index]); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_KEYWORD_PARAMETER_NODE: { - yp_buffer_append_str(buffer, "KeywordParameterNode(", 21); - prettyprint_token(buffer, &((yp_keyword_parameter_node_t *)node)->name); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_keyword_parameter_node_t *)node)->value == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_keyword_parameter_node_t *)node)->value); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_KEYWORD_REST_PARAMETER_NODE: { - yp_buffer_append_str(buffer, "KeywordRestParameterNode(", 25); - prettyprint_token(buffer, &((yp_keyword_rest_parameter_node_t *)node)->operator); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_keyword_rest_parameter_node_t *)node)->name.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_keyword_rest_parameter_node_t *)node)->name); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_LAMBDA_NODE: { - yp_buffer_append_str(buffer, "LambdaNode(", 11); - for (uint32_t index = 0; index < ((yp_lambda_node_t *)node)->locals.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_token(buffer, &((yp_lambda_node_t *)node)->locals.tokens[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_lambda_node_t *)node)->opening); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_lambda_node_t *)node)->parameters == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_lambda_node_t *)node)->parameters); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_lambda_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_lambda_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_LOCAL_VARIABLE_READ_NODE: { - yp_buffer_append_str(buffer, "LocalVariableReadNode(", 22); - char depth_buffer[12]; - snprintf(depth_buffer, 12, "+%d", ((yp_local_variable_read_node_t *)node)->depth); - yp_buffer_append_str(buffer, depth_buffer, strlen(depth_buffer)); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_LOCAL_VARIABLE_WRITE_NODE: { - yp_buffer_append_str(buffer, "LocalVariableWriteNode(", 23); - prettyprint_location(buffer, parser, &((yp_local_variable_write_node_t *)node)->name_loc); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_local_variable_write_node_t *)node)->value == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_local_variable_write_node_t *)node)->value); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_local_variable_write_node_t *)node)->operator_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_local_variable_write_node_t *)node)->operator_loc); - } - yp_buffer_append_str(buffer, ", ", 2); char depth_buffer[12]; - snprintf(depth_buffer, 12, "+%d", ((yp_local_variable_write_node_t *)node)->depth); - yp_buffer_append_str(buffer, depth_buffer, strlen(depth_buffer)); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_MATCH_PREDICATE_NODE: { - yp_buffer_append_str(buffer, "MatchPredicateNode(", 19); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->value); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->pattern); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_match_predicate_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_MATCH_REQUIRED_NODE: { - yp_buffer_append_str(buffer, "MatchRequiredNode(", 18); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_match_required_node_t *)node)->value); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_match_required_node_t *)node)->pattern); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_match_required_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_MISSING_NODE: { - yp_buffer_append_str(buffer, "MissingNode(", 12); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_MODULE_NODE: { - yp_buffer_append_str(buffer, "ModuleNode(", 11); - for (uint32_t index = 0; index < ((yp_module_node_t *)node)->locals.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_token(buffer, &((yp_module_node_t *)node)->locals.tokens[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_module_node_t *)node)->module_keyword); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_module_node_t *)node)->constant_path); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_module_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_module_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_module_node_t *)node)->end_keyword); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_MULTI_WRITE_NODE: { - yp_buffer_append_str(buffer, "MultiWriteNode(", 15); - for (uint32_t index = 0; index < ((yp_multi_write_node_t *)node)->targets.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_multi_write_node_t *) node)->targets.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_multi_write_node_t *)node)->operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_multi_write_node_t *)node)->operator); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_multi_write_node_t *)node)->value == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_multi_write_node_t *)node)->value); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_multi_write_node_t *)node)->lparen_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_multi_write_node_t *)node)->lparen_loc); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_multi_write_node_t *)node)->rparen_loc.start == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_location(buffer, parser, &((yp_multi_write_node_t *)node)->rparen_loc); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_NEXT_NODE: { - yp_buffer_append_str(buffer, "NextNode(", 9); - if (((yp_next_node_t *)node)->arguments == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_next_node_t *)node)->arguments); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_next_node_t *)node)->keyword_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_NIL_NODE: { - yp_buffer_append_str(buffer, "NilNode(", 8); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_NO_KEYWORDS_PARAMETER_NODE: { - yp_buffer_append_str(buffer, "NoKeywordsParameterNode(", 24); - prettyprint_location(buffer, parser, &((yp_no_keywords_parameter_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_no_keywords_parameter_node_t *)node)->keyword_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_OPERATOR_AND_ASSIGNMENT_NODE: { - yp_buffer_append_str(buffer, "OperatorAndAssignmentNode(", 26); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_operator_and_assignment_node_t *)node)->target); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_operator_and_assignment_node_t *)node)->value); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_operator_and_assignment_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_OPERATOR_ASSIGNMENT_NODE: { - yp_buffer_append_str(buffer, "OperatorAssignmentNode(", 23); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_operator_assignment_node_t *)node)->target); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_operator_assignment_node_t *)node)->operator); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_operator_assignment_node_t *)node)->value); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_OPERATOR_OR_ASSIGNMENT_NODE: { - yp_buffer_append_str(buffer, "OperatorOrAssignmentNode(", 25); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_operator_or_assignment_node_t *)node)->target); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_operator_or_assignment_node_t *)node)->value); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_operator_or_assignment_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_OPTIONAL_PARAMETER_NODE: { - yp_buffer_append_str(buffer, "OptionalParameterNode(", 22); - prettyprint_token(buffer, &((yp_optional_parameter_node_t *)node)->name); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_optional_parameter_node_t *)node)->equal_operator); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_optional_parameter_node_t *)node)->value); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_OR_NODE: { - yp_buffer_append_str(buffer, "OrNode(", 7); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_or_node_t *)node)->left); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_or_node_t *)node)->right); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_or_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_PARAMETERS_NODE: { - yp_buffer_append_str(buffer, "ParametersNode(", 15); - for (uint32_t index = 0; index < ((yp_parameters_node_t *)node)->requireds.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_parameters_node_t *) node)->requireds.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_parameters_node_t *)node)->optionals.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_parameters_node_t *) node)->optionals.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_parameters_node_t *)node)->posts.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_parameters_node_t *) node)->posts.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_parameters_node_t *)node)->rest == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_parameters_node_t *)node)->rest); - } - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_parameters_node_t *)node)->keywords.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_parameters_node_t *) node)->keywords.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_parameters_node_t *)node)->keyword_rest == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_parameters_node_t *)node)->keyword_rest); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_parameters_node_t *)node)->block == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_parameters_node_t *)node)->block); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_PARENTHESES_NODE: { - yp_buffer_append_str(buffer, "ParenthesesNode(", 16); - if (((yp_parentheses_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_parentheses_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_parentheses_node_t *)node)->opening_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_parentheses_node_t *)node)->closing_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_PINNED_EXPRESSION_NODE: { - yp_buffer_append_str(buffer, "PinnedExpressionNode(", 21); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_pinned_expression_node_t *)node)->expression); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pinned_expression_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pinned_expression_node_t *)node)->lparen_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pinned_expression_node_t *)node)->rparen_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_PINNED_VARIABLE_NODE: { - yp_buffer_append_str(buffer, "PinnedVariableNode(", 19); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_pinned_variable_node_t *)node)->variable); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pinned_variable_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_POST_EXECUTION_NODE: { - yp_buffer_append_str(buffer, "PostExecutionNode(", 18); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_post_execution_node_t *)node)->statements); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_post_execution_node_t *)node)->keyword_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_post_execution_node_t *)node)->opening_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_post_execution_node_t *)node)->closing_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_PRE_EXECUTION_NODE: { - yp_buffer_append_str(buffer, "PreExecutionNode(", 17); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_pre_execution_node_t *)node)->statements); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pre_execution_node_t *)node)->keyword_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pre_execution_node_t *)node)->opening_loc); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_pre_execution_node_t *)node)->closing_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_PROGRAM_NODE: { - yp_buffer_append_str(buffer, "ProgramNode(", 12); - for (uint32_t index = 0; index < ((yp_program_node_t *)node)->locals.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_token(buffer, &((yp_program_node_t *)node)->locals.tokens[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_program_node_t *)node)->statements); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_RANGE_NODE: { - yp_buffer_append_str(buffer, "RangeNode(", 10); - if (((yp_range_node_t *)node)->left == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_range_node_t *)node)->left); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_range_node_t *)node)->right == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_range_node_t *)node)->right); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_range_node_t *)node)->operator_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_RATIONAL_NODE: { - yp_buffer_append_str(buffer, "RationalNode(", 13); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_rational_node_t *)node)->numeric); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_REDO_NODE: { - yp_buffer_append_str(buffer, "RedoNode(", 9); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_REGULAR_EXPRESSION_NODE: { - yp_buffer_append_str(buffer, "RegularExpressionNode(", 22); - prettyprint_token(buffer, &((yp_regular_expression_node_t *)node)->opening); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_regular_expression_node_t *)node)->content); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_regular_expression_node_t *)node)->closing); - yp_buffer_append_str(buffer, ", ", 2); yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, yp_string_source(&((yp_regular_expression_node_t *)node)->unescaped), yp_string_length(&((yp_regular_expression_node_t *)node)->unescaped)); - yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE: { - yp_buffer_append_str(buffer, "RequiredDestructuredParameterNode(", 34); - for (uint32_t index = 0; index < ((yp_required_destructured_parameter_node_t *)node)->parameters.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_required_destructured_parameter_node_t *) node)->parameters.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_required_destructured_parameter_node_t *)node)->opening); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_required_destructured_parameter_node_t *)node)->closing); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_REQUIRED_PARAMETER_NODE: { - yp_buffer_append_str(buffer, "RequiredParameterNode(", 22); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_RESCUE_MODIFIER_NODE: { - yp_buffer_append_str(buffer, "RescueModifierNode(", 19); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->expression); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_rescue_modifier_node_t *)node)->rescue_keyword); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->rescue_expression); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_RESCUE_NODE: { - yp_buffer_append_str(buffer, "RescueNode(", 11); - prettyprint_token(buffer, &((yp_rescue_node_t *)node)->rescue_keyword); - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_rescue_node_t *)node)->exceptions.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_rescue_node_t *) node)->exceptions.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_rescue_node_t *)node)->equal_greater.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_rescue_node_t *)node)->equal_greater); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_rescue_node_t *)node)->exception == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_rescue_node_t *)node)->exception); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_rescue_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_rescue_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_rescue_node_t *)node)->consequent == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_rescue_node_t *)node)->consequent); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_REST_PARAMETER_NODE: { - yp_buffer_append_str(buffer, "RestParameterNode(", 18); - prettyprint_token(buffer, &((yp_rest_parameter_node_t *)node)->operator); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_rest_parameter_node_t *)node)->name.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_rest_parameter_node_t *)node)->name); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_RETRY_NODE: { - yp_buffer_append_str(buffer, "RetryNode(", 10); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_RETURN_NODE: { - yp_buffer_append_str(buffer, "ReturnNode(", 11); - prettyprint_token(buffer, &((yp_return_node_t *)node)->keyword); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_return_node_t *)node)->arguments == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_return_node_t *)node)->arguments); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_SELF_NODE: { - yp_buffer_append_str(buffer, "SelfNode(", 9); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_SINGLETON_CLASS_NODE: { - yp_buffer_append_str(buffer, "SingletonClassNode(", 19); - for (uint32_t index = 0; index < ((yp_singleton_class_node_t *)node)->locals.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_token(buffer, &((yp_singleton_class_node_t *)node)->locals.tokens[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_singleton_class_node_t *)node)->class_keyword); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_singleton_class_node_t *)node)->operator); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->expression); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_singleton_class_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_singleton_class_node_t *)node)->end_keyword); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_SOURCE_ENCODING_NODE: { - yp_buffer_append_str(buffer, "SourceEncodingNode(", 19); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_SOURCE_FILE_NODE: { - yp_buffer_append_str(buffer, "SourceFileNode(", 15); - yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, yp_string_source(&((yp_source_file_node_t *)node)->filepath), yp_string_length(&((yp_source_file_node_t *)node)->filepath)); - yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_SOURCE_LINE_NODE: { - yp_buffer_append_str(buffer, "SourceLineNode(", 15); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_SPLAT_NODE: { - yp_buffer_append_str(buffer, "SplatNode(", 10); - prettyprint_token(buffer, &((yp_splat_node_t *)node)->operator); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_splat_node_t *)node)->expression == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_splat_node_t *)node)->expression); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_STATEMENTS_NODE: { - yp_buffer_append_str(buffer, "StatementsNode(", 15); - for (uint32_t index = 0; index < ((yp_statements_node_t *)node)->body.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_statements_node_t *) node)->body.nodes[index]); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_STRING_CONCAT_NODE: { - yp_buffer_append_str(buffer, "StringConcatNode(", 17); - prettyprint_node(buffer, parser, (yp_node_t *)((yp_string_concat_node_t *)node)->left); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_string_concat_node_t *)node)->right); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_STRING_INTERPOLATED_NODE: { - yp_buffer_append_str(buffer, "StringInterpolatedNode(", 23); - prettyprint_token(buffer, &((yp_string_interpolated_node_t *)node)->opening); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_string_interpolated_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_string_interpolated_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_string_interpolated_node_t *)node)->closing); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_STRING_NODE: { - yp_buffer_append_str(buffer, "StringNode(", 11); - if (((yp_string_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_string_node_t *)node)->opening); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_string_node_t *)node)->content); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_string_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_string_node_t *)node)->closing); - } - yp_buffer_append_str(buffer, ", ", 2); yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, yp_string_source(&((yp_string_node_t *)node)->unescaped), yp_string_length(&((yp_string_node_t *)node)->unescaped)); - yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_SUPER_NODE: { - yp_buffer_append_str(buffer, "SuperNode(", 10); - prettyprint_token(buffer, &((yp_super_node_t *)node)->keyword); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_super_node_t *)node)->lparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_super_node_t *)node)->lparen); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_super_node_t *)node)->arguments == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_super_node_t *)node)->arguments); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_super_node_t *)node)->rparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_super_node_t *)node)->rparen); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_super_node_t *)node)->block == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_super_node_t *)node)->block); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_SYMBOL_NODE: { - yp_buffer_append_str(buffer, "SymbolNode(", 11); - if (((yp_symbol_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_symbol_node_t *)node)->opening); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_symbol_node_t *)node)->value); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_symbol_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_symbol_node_t *)node)->closing); - } - yp_buffer_append_str(buffer, ", ", 2); yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, yp_string_source(&((yp_symbol_node_t *)node)->unescaped), yp_string_length(&((yp_symbol_node_t *)node)->unescaped)); - yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_TRUE_NODE: { - yp_buffer_append_str(buffer, "TrueNode(", 9); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_UNDEF_NODE: { - yp_buffer_append_str(buffer, "UndefNode(", 10); - for (uint32_t index = 0; index < ((yp_undef_node_t *)node)->names.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_undef_node_t *) node)->names.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); prettyprint_location(buffer, parser, &((yp_undef_node_t *)node)->keyword_loc); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_UNLESS_NODE: { - yp_buffer_append_str(buffer, "UnlessNode(", 11); - prettyprint_token(buffer, &((yp_unless_node_t *)node)->keyword); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_unless_node_t *)node)->predicate); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_unless_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_unless_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_unless_node_t *)node)->consequent == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_unless_node_t *)node)->consequent); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_unless_node_t *)node)->end_keyword.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_unless_node_t *)node)->end_keyword); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_UNTIL_NODE: { - yp_buffer_append_str(buffer, "UntilNode(", 10); - prettyprint_token(buffer, &((yp_until_node_t *)node)->keyword); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_until_node_t *)node)->predicate); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_until_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_until_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_WHEN_NODE: { - yp_buffer_append_str(buffer, "WhenNode(", 9); - prettyprint_token(buffer, &((yp_when_node_t *)node)->when_keyword); - yp_buffer_append_str(buffer, ", ", 2); for (uint32_t index = 0; index < ((yp_when_node_t *)node)->conditions.size; index++) { - if (index != 0) yp_buffer_append_str(buffer, ", ", 2); - prettyprint_node(buffer, parser, (yp_node_t *) ((yp_when_node_t *) node)->conditions.nodes[index]); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_when_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_when_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_WHILE_NODE: { - yp_buffer_append_str(buffer, "WhileNode(", 10); - prettyprint_token(buffer, &((yp_while_node_t *)node)->keyword); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_node(buffer, parser, (yp_node_t *)((yp_while_node_t *)node)->predicate); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_while_node_t *)node)->statements == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_while_node_t *)node)->statements); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_X_STRING_NODE: { - yp_buffer_append_str(buffer, "XStringNode(", 12); - prettyprint_token(buffer, &((yp_x_string_node_t *)node)->opening); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_x_string_node_t *)node)->content); - yp_buffer_append_str(buffer, ", ", 2); prettyprint_token(buffer, &((yp_x_string_node_t *)node)->closing); - yp_buffer_append_str(buffer, ", ", 2); yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, yp_string_source(&((yp_x_string_node_t *)node)->unescaped), yp_string_length(&((yp_x_string_node_t *)node)->unescaped)); - yp_buffer_append_str(buffer, "\"", 1); - yp_buffer_append_str(buffer, ")", 1); - break; - } - case YP_NODE_YIELD_NODE: { - yp_buffer_append_str(buffer, "YieldNode(", 10); - prettyprint_token(buffer, &((yp_yield_node_t *)node)->keyword); - yp_buffer_append_str(buffer, ", ", 2); if (((yp_yield_node_t *)node)->lparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_yield_node_t *)node)->lparen); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_yield_node_t *)node)->arguments == NULL) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_node(buffer, parser, (yp_node_t *)((yp_yield_node_t *)node)->arguments); - } - yp_buffer_append_str(buffer, ", ", 2); if (((yp_yield_node_t *)node)->rparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_str(buffer, "nil", 3); - } else { - prettyprint_token(buffer, &((yp_yield_node_t *)node)->rparen); - } - yp_buffer_append_str(buffer, ")", 1); - break; - } - } -} - -void -yp_print_node(yp_parser_t *parser, yp_node_t *node) { - yp_buffer_t buffer; - yp_buffer_init(&buffer); - - prettyprint_node(&buffer, parser, node); - printf("%.*s\n", (int) buffer.length, buffer.value); - - yp_buffer_free(&buffer); -} - -// Pretty-prints the AST represented by the given node to the given buffer. -__attribute__((__visibility__("default"))) extern void -yp_prettyprint(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer) { - prettyprint_node(buffer, parser, node); -} diff --git a/src/main/c/yarp/src/yarp/src/regexp.c b/src/main/c/yarp/src/yarp/src/regexp.c deleted file mode 100644 index d7726ea55052..000000000000 --- a/src/main/c/yarp/src/yarp/src/regexp.c +++ /dev/null @@ -1,518 +0,0 @@ -#include "yarp/regexp.h" - -// This is the parser that is going to handle parsing regular expressions. -typedef struct { - const char *start; - const char *cursor; - const char *end; - yp_string_list_t *named_captures; -} yp_regexp_parser_t; - -// This initializes a new parser with the given source. -static void -yp_regexp_parser_init(yp_regexp_parser_t *parser, const char *start, const char *end, yp_string_list_t *named_captures) { - *parser = (yp_regexp_parser_t) { - .start = start, - .cursor = start, - .end = end, - .named_captures = named_captures - }; -} - -// This appends a new string to the list of named captures. -static void -yp_regexp_parser_named_capture(yp_regexp_parser_t *parser, const char *start, const char *end) { - yp_string_t string; - yp_string_shared_init(&string, start, end); - yp_string_list_append(parser->named_captures, &string); - yp_string_free(&string); -} - -// Returns true if the next character is the end of the source. -static inline bool -yp_regexp_char_is_eof(yp_regexp_parser_t *parser) { - return parser->cursor >= parser->end; -} - -// Optionally accept a char and consume it if it exists. -static inline bool -yp_regexp_char_accept(yp_regexp_parser_t *parser, char value) { - if (!yp_regexp_char_is_eof(parser) && *parser->cursor == value) { - parser->cursor++; - return true; - } - return false; -} - -// Expect a character to be present and consume it. -static inline bool -yp_regexp_char_expect(yp_regexp_parser_t *parser, char value) { - if (!yp_regexp_char_is_eof(parser) && *parser->cursor == value) { - parser->cursor++; - return true; - } - return false; -} - -// This advances the current token to the next instance of the given character. -static bool -yp_regexp_char_find(yp_regexp_parser_t *parser, char value) { - const char *end = (const char *) memchr(parser->cursor, value, (size_t) (parser->end - parser->cursor)); - if (end == NULL) { - return false; - } - - parser->cursor = end + 1; - return true; -} - -// Range quantifiers are a special class of quantifiers that look like -// -// * {digit} -// * {digit,} -// * {digit,digit} -// * {,digit} -// -// Unfortunately, if there are any spaces in between, then this just becomes a -// regular character match expression and we have to backtrack. So when this -// function first starts running, we'll create a "save" point and then attempt -// to parse the quantifier. If it fails, we'll restore the save point and -// return. -// -// The properly track everything, we're going to build a little state machine. -// It looks something like the following: -// -// ┌───────┐ ┌─────────┐ ────────────┐ -// ──── lbrace ───> │ start │ ──── digit ───> │ minimum │ │ -// └───────┘ └─────────┘ <─── digit ─┘ -// │ │ │ -// ┌───────┐ │ │ rbrace -// │ comma │ <───── comma ┌──── comma ───────┘ │ -// └───────┘ V V -// │ ┌─────────┐ ┌─────────┐ -// └── digit ──> │ maximum │ ── rbrace ──> │| final |│ -// └─────────┘ └─────────┘ -// │ ^ -// └─ digit ─┘ -// -// Note that by the time we've hit this function, the lbrace has already been -// consumed so we're in the start state. -static bool -yp_regexp_parse_range_quantifier(yp_regexp_parser_t *parser) { - const char *savepoint = parser->cursor; - - enum { - YP_REGEXP_RANGE_QUANTIFIER_STATE_START, - YP_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM, - YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM, - YP_REGEXP_RANGE_QUANTIFIER_STATE_COMMA - } state = YP_REGEXP_RANGE_QUANTIFIER_STATE_START; - - while (1) { - switch (state) { - case YP_REGEXP_RANGE_QUANTIFIER_STATE_START: - switch (*parser->cursor) { - case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - parser->cursor++; - state = YP_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM; - break; - case ',': - parser->cursor++; - state = YP_REGEXP_RANGE_QUANTIFIER_STATE_COMMA; - break; - default: - parser->cursor = savepoint; - return true; - } - break; - case YP_REGEXP_RANGE_QUANTIFIER_STATE_MINIMUM: - switch (*parser->cursor) { - case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - parser->cursor++; - break; - case ',': - parser->cursor++; - state = YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM; - break; - case '}': - parser->cursor++; - return true; - default: - parser->cursor = savepoint; - return true; - } - break; - case YP_REGEXP_RANGE_QUANTIFIER_STATE_COMMA: - switch (*parser->cursor) { - case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - parser->cursor++; - state = YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM; - break; - default: - parser->cursor = savepoint; - return true; - } - break; - case YP_REGEXP_RANGE_QUANTIFIER_STATE_MAXIMUM: - switch (*parser->cursor) { - case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': - parser->cursor++; - break; - case '}': - parser->cursor++; - return true; - default: - parser->cursor = savepoint; - return true; - } - break; - } - } - - return true; -} - -// quantifier : star-quantifier -// | plus-quantifier -// | optional-quantifier -// | range-quantifier -// | -// ; -static bool -yp_regexp_parse_quantifier(yp_regexp_parser_t *parser) { - switch (*parser->cursor) { - case '*': - case '+': - case '?': - parser->cursor++; - return true; - case '{': - parser->cursor++; - return yp_regexp_parse_range_quantifier(parser); - default: - // In this case there is no quantifier. - return true; - } -} - -// match-posix-class : '[' '[' ':' '^'? CHAR+ ':' ']' ']' -// ; -static bool -yp_regexp_parse_posix_class(yp_regexp_parser_t *parser) { - if (!yp_regexp_char_expect(parser, ':')) { - return false; - } - - yp_regexp_char_accept(parser, '^'); - - return ( - yp_regexp_char_find(parser, ':') && - yp_regexp_char_expect(parser, ']') && - yp_regexp_char_expect(parser, ']') - ); -} - -// Forward declaration because character sets can be nested. -static bool -yp_regexp_parse_lbracket(yp_regexp_parser_t *parser); - -// match-char-set : '[' '^'? (match-range | match-char)* ']' -// ; -static bool -yp_regexp_parse_character_set(yp_regexp_parser_t *parser) { - yp_regexp_char_accept(parser, '^'); - - while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ']') { - switch (*parser->cursor++) { - case '[': - yp_regexp_parse_lbracket(parser); - break; - case '\\': - if (!yp_regexp_char_is_eof(parser)) { - parser->cursor++; - } - break; - default: - // do nothing, we've already advanced the cursor - break; - } - } - - return yp_regexp_char_expect(parser, ']'); -} - -// A left bracket can either mean a POSIX class or a character set. -static bool -yp_regexp_parse_lbracket(yp_regexp_parser_t *parser) { - const char *reset = parser->cursor; - - if ((parser->cursor + 2 < parser->end) && parser->cursor[0] == '[' && parser->cursor[1] == ':') { - parser->cursor++; - if (yp_regexp_parse_posix_class(parser)) return true; - - parser->cursor = reset; - } - - return yp_regexp_parse_character_set(parser); -} - -// Forward declaration here since parsing groups needs to go back up the grammar -// to parse expressions within them. -static bool -yp_regexp_parse_expression(yp_regexp_parser_t *parser); - -// These are the states of the options that are configurable on the regular -// expression (or from within a group). -typedef enum { - YP_REGEXP_OPTION_STATE_INVALID, - YP_REGEXP_OPTION_STATE_TOGGLEABLE, - YP_REGEXP_OPTION_STATE_ADDABLE -} yp_regexp_option_state_t; - -// This is the set of options that are configurable on the regular expression. -typedef yp_regexp_option_state_t yp_regexp_options_t[128]; - -// Initialize a new set of options to their default values. -static void -yp_regexp_options_init(yp_regexp_options_t *options) { - memset(options, YP_REGEXP_OPTION_STATE_INVALID, sizeof(yp_regexp_option_state_t) * 128); - (*options)['i'] = YP_REGEXP_OPTION_STATE_TOGGLEABLE; - (*options)['m'] = YP_REGEXP_OPTION_STATE_TOGGLEABLE; - (*options)['x'] = YP_REGEXP_OPTION_STATE_TOGGLEABLE; - (*options)['d'] = YP_REGEXP_OPTION_STATE_ADDABLE; - (*options)['a'] = YP_REGEXP_OPTION_STATE_ADDABLE; - (*options)['u'] = YP_REGEXP_OPTION_STATE_ADDABLE; -} - -// Attempt to add the given option to the set of options. Returns true if it was -// added, false if it was already present. -static bool -yp_regexp_options_add(yp_regexp_options_t *options, unsigned char option) { - switch ((*options)[option]) { - case YP_REGEXP_OPTION_STATE_INVALID: - return false; - case YP_REGEXP_OPTION_STATE_TOGGLEABLE: - case YP_REGEXP_OPTION_STATE_ADDABLE: - (*options)[option] = YP_REGEXP_OPTION_STATE_INVALID; - return true; - } - return false; -} - -// Attempt to remove the given option from the set of options. Returns true if -// it was removed, false if it was already absent. -static bool -yp_regexp_options_remove(yp_regexp_options_t *options, unsigned char option) { - switch ((*options)[option]) { - case YP_REGEXP_OPTION_STATE_INVALID: - case YP_REGEXP_OPTION_STATE_ADDABLE: - return false; - case YP_REGEXP_OPTION_STATE_TOGGLEABLE: - (*options)[option] = YP_REGEXP_OPTION_STATE_INVALID; - return true; - } - return false; -} - -// Groups can have quite a few different patterns for syntax. They basically -// just wrap a set of expressions, but they can potentially have options after a -// question mark. If there _isn't_ a question mark, then it's just a set of -// expressions. If there _is_, then here are the options: -// -// * (?#...) - inline comments -// * (?:subexp) - non-capturing group -// * (?=subexp) - positive lookahead -// * (?!subexp) - negative lookahead -// * (?>subexp) - atomic group -// * (?~subexp) - absence operator -// * (?<=subexp) - positive lookbehind -// * (?subexp) - named capturing group -// * (?'name'subexp) - named capturing group -// * (?(cond)yes-subexp) - conditional expression -// * (?(cond)yes-subexp|no-subexp) - conditional expression -// * (?imxdau-imx) - turn on and off configuration -// * (?imxdau-imx:subexp) - turn on and off configuration for an expression -// -static bool -yp_regexp_parse_group(yp_regexp_parser_t *parser) { - // First, parse any options for the group. - if (yp_regexp_char_accept(parser, '?')) { - yp_regexp_options_t options; - yp_regexp_options_init(&options); - - switch (*parser->cursor) { - case '#': { // inline comments - bool found = yp_regexp_char_find(parser, ')'); - // the close paren we found is escaped, we need to find another - while (parser->start <= parser->cursor - 2 && *(parser->cursor - 2) == '\\') { - found = yp_regexp_char_find(parser, ')'); - } - return found; - } - case ':': // non-capturing group - case '=': // positive lookahead - case '!': // negative lookahead - case '>': // atomic group - case '~': // absence operator - parser->cursor++; - break; - case '<': - parser->cursor++; - if (yp_regexp_char_is_eof(parser)) { - return false; - } - - switch (*parser->cursor) { - case '=': // positive lookbehind - case '!': // negative lookbehind - parser->cursor++; - break; - default: { // named capture group - const char *start = parser->cursor; - if (!yp_regexp_char_find(parser, '>')) { - return false; - } - yp_regexp_parser_named_capture(parser, start, parser->cursor - 1); - break; - } - } - break; - case '\'': { // named capture group - const char *start = ++parser->cursor; - if (!yp_regexp_char_find(parser, '\'')) { - return false; - } - - yp_regexp_parser_named_capture(parser, start, parser->cursor - 1); - break; - } - case '(': // conditional expression - if (!yp_regexp_char_find(parser, ')')) { - return false; - } - break; - case 'i': case 'm': case 'x': case 'd': case 'a': case 'u': // options - while (!yp_regexp_char_is_eof(parser) && *parser->cursor != '-' && *parser->cursor != ':' && *parser->cursor != ')') { - if (!yp_regexp_options_add(&options, (unsigned char) *parser->cursor)) { - return false; - } - parser->cursor++; - } - - if (yp_regexp_char_is_eof(parser)) { - return false; - } - - // If we hit a -, then we're done parsing options. - if (*parser->cursor != '-') break; - - // Otherwise, fallthrough to the - case. - /* fallthrough */ - case '-': - parser->cursor++; - while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ':' && *parser->cursor != ')') { - if (!yp_regexp_options_remove(&options, (unsigned char) *parser->cursor)) { - return false; - } - parser->cursor++; - } - - if (yp_regexp_char_is_eof(parser)) { - return false; - } - break; - default: - return false; - } - } - - // Now, parse the expressions within this group. - while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ')') { - if (!yp_regexp_parse_expression(parser)) { - return false; - } - yp_regexp_char_accept(parser, '|'); - } - - // Finally, make sure we have a closing parenthesis. - return yp_regexp_char_expect(parser, ')'); -} - -// item : anchor -// | match-posix-class -// | match-char-set -// | match-char-class -// | match-char-prop -// | match-char -// | match-any -// | group -// | quantified -// ; -static bool -yp_regexp_parse_item(yp_regexp_parser_t *parser) { - switch (*parser->cursor++) { - case '^': - case '$': - return true; - case '\\': - if (!yp_regexp_char_is_eof(parser)) { - parser->cursor++; - } - return yp_regexp_parse_quantifier(parser); - case '(': - return yp_regexp_parse_group(parser) && yp_regexp_parse_quantifier(parser); - case '[': - return yp_regexp_parse_lbracket(parser) && yp_regexp_parse_quantifier(parser); - default: - return yp_regexp_parse_quantifier(parser); - } -} - -// expression : item+ -// ; -static bool -yp_regexp_parse_expression(yp_regexp_parser_t *parser) { - if (!yp_regexp_parse_item(parser)) { - return false; - } - - while (!yp_regexp_char_is_eof(parser) && *parser->cursor != ')' && *parser->cursor != '|') { - if (!yp_regexp_parse_item(parser)) { - return false; - } - } - - return true; -} - -// pattern : EOF -// | expression EOF -// | expression '|' pattern -// ; -static bool -yp_regexp_parse_pattern(yp_regexp_parser_t *parser) { - return ( - ( - // Exit early if the pattern is empty. - yp_regexp_char_is_eof(parser) || - // Parse the first expression in the pattern. - yp_regexp_parse_expression(parser) - ) && - ( - // Return now if we've parsed the entire pattern. - yp_regexp_char_is_eof(parser) || - // Otherwise, we should have a pipe character. - (yp_regexp_char_expect(parser, '|') && yp_regexp_parse_pattern(parser)) - ) - ); -} - -// Parse a regular expression and extract the names of all of the named capture -// groups. -__attribute__((__visibility__("default"))) extern bool -yp_regexp_named_capture_group_names(const char *source, size_t size, yp_string_list_t *named_captures) { - yp_regexp_parser_t parser; - yp_regexp_parser_init(&parser, source, source + size, named_captures); - return yp_regexp_parse_pattern(&parser); -} diff --git a/src/main/c/yarp/src/yarp/src/serialize.c b/src/main/c/yarp/src/yarp/src/serialize.c deleted file mode 100644 index 384ff2861b5b..000000000000 --- a/src/main/c/yarp/src/yarp/src/serialize.c +++ /dev/null @@ -1,1303 +0,0 @@ -/******************************************************************************/ -/* This file is generated by the bin/template script and should not be */ -/* modified manually. See */ -/* bin/templates/src/serialize.c.erb */ -/* if you are looking to modify the */ -/* template */ -/******************************************************************************/ -#include "yarp/ast.h" -#include "yarp/parser.h" -#include "yarp/util/yp_buffer.h" -#include "yarp/util/yp_conversion.h" - -static void -serialize_token(yp_parser_t *parser, yp_token_t *token, yp_buffer_t *buffer) { - assert(token->start); - assert(token->end); - assert(token->start <= token->end); - - yp_buffer_append_u8(buffer, token->type); - yp_buffer_append_u32(buffer, yp_long_to_u32(token->start - parser->start)); - yp_buffer_append_u32(buffer, yp_long_to_u32(token->end - token->start)); -} - -static void -serialize_location(yp_parser_t *parser, yp_location_t *location, yp_buffer_t *buffer) { - assert(location->start); - assert(location->end); - assert(location->start <= location->end); - - yp_buffer_append_u32(buffer, yp_long_to_u32(location->start - parser->start)); - yp_buffer_append_u32(buffer, yp_long_to_u32(location->end - location->start)); -} - -void -yp_serialize_node(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer) { - yp_buffer_append_u8(buffer, node->type); - - size_t offset = buffer->length; - - serialize_location(parser, &node->location, buffer); - - switch (node->type) { - case YP_NODE_ALIAS_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_alias_node_t *)node)->new_name, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_alias_node_t *)node)->old_name, buffer); - serialize_location(parser, &((yp_alias_node_t *)node)->keyword_loc, buffer); - break; - } - case YP_NODE_ALTERNATION_PATTERN_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->left, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_alternation_pattern_node_t *)node)->right, buffer); - serialize_location(parser, &((yp_alternation_pattern_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_AND_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_and_node_t *)node)->left, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_and_node_t *)node)->right, buffer); - serialize_token(parser, &((yp_and_node_t *)node)->operator, buffer); - break; - } - case YP_NODE_ARGUMENTS_NODE: { - uint32_t arguments_size = yp_ulong_to_u32(((yp_arguments_node_t *)node)->arguments.size); - yp_buffer_append_u32(buffer, arguments_size); - for (uint32_t index = 0; index < arguments_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_arguments_node_t *)node)->arguments.nodes[index], buffer); - } - break; - } - case YP_NODE_ARRAY_NODE: { - uint32_t elements_size = yp_ulong_to_u32(((yp_array_node_t *)node)->elements.size); - yp_buffer_append_u32(buffer, elements_size); - for (uint32_t index = 0; index < elements_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_array_node_t *)node)->elements.nodes[index], buffer); - } - if (((yp_array_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_array_node_t *)node)->opening, buffer); - } - if (((yp_array_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_array_node_t *)node)->closing, buffer); - } - break; - } - case YP_NODE_ARRAY_PATTERN_NODE: { - if (((yp_array_pattern_node_t *)node)->constant == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->constant, buffer); - } - uint32_t requireds_size = yp_ulong_to_u32(((yp_array_pattern_node_t *)node)->requireds.size); - yp_buffer_append_u32(buffer, requireds_size); - for (uint32_t index = 0; index < requireds_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_array_pattern_node_t *)node)->requireds.nodes[index], buffer); - } - if (((yp_array_pattern_node_t *)node)->rest == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_array_pattern_node_t *)node)->rest, buffer); - } - uint32_t posts_size = yp_ulong_to_u32(((yp_array_pattern_node_t *)node)->posts.size); - yp_buffer_append_u32(buffer, posts_size); - for (uint32_t index = 0; index < posts_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_array_pattern_node_t *)node)->posts.nodes[index], buffer); - } - if (((yp_array_pattern_node_t *)node)->opening_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_array_pattern_node_t *)node)->opening_loc, buffer); - } - if (((yp_array_pattern_node_t *)node)->closing_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_array_pattern_node_t *)node)->closing_loc, buffer); - } - break; - } - case YP_NODE_ASSOC_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_assoc_node_t *)node)->key, buffer); - if (((yp_assoc_node_t *)node)->value == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_assoc_node_t *)node)->value, buffer); - } - if (((yp_assoc_node_t *)node)->operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_assoc_node_t *)node)->operator, buffer); - } - break; - } - case YP_NODE_ASSOC_SPLAT_NODE: { - if (((yp_assoc_splat_node_t *)node)->value == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_assoc_splat_node_t *)node)->value, buffer); - } - serialize_location(parser, &((yp_assoc_splat_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_BEGIN_NODE: { - if (((yp_begin_node_t *)node)->begin_keyword.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_begin_node_t *)node)->begin_keyword, buffer); - } - if (((yp_begin_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_begin_node_t *)node)->statements, buffer); - } - if (((yp_begin_node_t *)node)->rescue_clause == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_begin_node_t *)node)->rescue_clause, buffer); - } - if (((yp_begin_node_t *)node)->else_clause == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_begin_node_t *)node)->else_clause, buffer); - } - if (((yp_begin_node_t *)node)->ensure_clause == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_begin_node_t *)node)->ensure_clause, buffer); - } - if (((yp_begin_node_t *)node)->end_keyword.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_begin_node_t *)node)->end_keyword, buffer); - } - break; - } - case YP_NODE_BLOCK_ARGUMENT_NODE: { - if (((yp_block_argument_node_t *)node)->expression == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_block_argument_node_t *)node)->expression, buffer); - } - serialize_location(parser, &((yp_block_argument_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_BLOCK_NODE: { - uint32_t locals_size = yp_ulong_to_u32(((yp_block_node_t *)node)->locals.size); - yp_buffer_append_u32(buffer, locals_size); - for (uint32_t index = 0; index < locals_size; index++) { - serialize_token(parser, &((yp_block_node_t *)node)->locals.tokens[index], buffer); - } - if (((yp_block_node_t *)node)->parameters == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_block_node_t *)node)->parameters, buffer); - } - if (((yp_block_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_block_node_t *)node)->statements, buffer); - } - serialize_location(parser, &((yp_block_node_t *)node)->opening_loc, buffer); - serialize_location(parser, &((yp_block_node_t *)node)->closing_loc, buffer); - break; - } - case YP_NODE_BLOCK_PARAMETER_NODE: { - if (((yp_block_parameter_node_t *)node)->name.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_block_parameter_node_t *)node)->name, buffer); - } - serialize_location(parser, &((yp_block_parameter_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_BLOCK_PARAMETERS_NODE: { - if (((yp_block_parameters_node_t *)node)->parameters == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_block_parameters_node_t *)node)->parameters, buffer); - } - uint32_t locals_size = yp_ulong_to_u32(((yp_block_parameters_node_t *)node)->locals.size); - yp_buffer_append_u32(buffer, locals_size); - for (uint32_t index = 0; index < locals_size; index++) { - serialize_token(parser, &((yp_block_parameters_node_t *)node)->locals.tokens[index], buffer); - } - if (((yp_block_parameters_node_t *)node)->opening_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_block_parameters_node_t *)node)->opening_loc, buffer); - } - if (((yp_block_parameters_node_t *)node)->closing_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_block_parameters_node_t *)node)->closing_loc, buffer); - } - break; - } - case YP_NODE_BREAK_NODE: { - if (((yp_break_node_t *)node)->arguments == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_break_node_t *)node)->arguments, buffer); - } - serialize_location(parser, &((yp_break_node_t *)node)->keyword_loc, buffer); - break; - } - case YP_NODE_CALL_NODE: { - if (((yp_call_node_t *)node)->receiver == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_call_node_t *)node)->receiver, buffer); - } - if (((yp_call_node_t *)node)->call_operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_call_node_t *)node)->call_operator, buffer); - } - if (((yp_call_node_t *)node)->message.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_call_node_t *)node)->message, buffer); - } - if (((yp_call_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_call_node_t *)node)->opening, buffer); - } - if (((yp_call_node_t *)node)->arguments == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_call_node_t *)node)->arguments, buffer); - } - if (((yp_call_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_call_node_t *)node)->closing, buffer); - } - if (((yp_call_node_t *)node)->block == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_call_node_t *)node)->block, buffer); - } - uint32_t name_length = yp_ulong_to_u32(yp_string_length(&((yp_call_node_t *)node)->name)); - yp_buffer_append_u32(buffer, name_length); - yp_buffer_append_str(buffer, yp_string_source(&((yp_call_node_t *)node)->name), name_length); - break; - } - case YP_NODE_CAPTURE_PATTERN_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->value, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_capture_pattern_node_t *)node)->target, buffer); - serialize_location(parser, &((yp_capture_pattern_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_CASE_NODE: { - if (((yp_case_node_t *)node)->predicate == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_case_node_t *)node)->predicate, buffer); - } - uint32_t conditions_size = yp_ulong_to_u32(((yp_case_node_t *)node)->conditions.size); - yp_buffer_append_u32(buffer, conditions_size); - for (uint32_t index = 0; index < conditions_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_case_node_t *)node)->conditions.nodes[index], buffer); - } - if (((yp_case_node_t *)node)->consequent == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_case_node_t *)node)->consequent, buffer); - } - serialize_location(parser, &((yp_case_node_t *)node)->case_keyword_loc, buffer); - serialize_location(parser, &((yp_case_node_t *)node)->end_keyword_loc, buffer); - break; - } - case YP_NODE_CLASS_NODE: { - uint32_t locals_size = yp_ulong_to_u32(((yp_class_node_t *)node)->locals.size); - yp_buffer_append_u32(buffer, locals_size); - for (uint32_t index = 0; index < locals_size; index++) { - serialize_token(parser, &((yp_class_node_t *)node)->locals.tokens[index], buffer); - } - serialize_token(parser, &((yp_class_node_t *)node)->class_keyword, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_class_node_t *)node)->constant_path, buffer); - if (((yp_class_node_t *)node)->inheritance_operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_class_node_t *)node)->inheritance_operator, buffer); - } - if (((yp_class_node_t *)node)->superclass == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_class_node_t *)node)->superclass, buffer); - } - if (((yp_class_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_class_node_t *)node)->statements, buffer); - } - serialize_token(parser, &((yp_class_node_t *)node)->end_keyword, buffer); - break; - } - case YP_NODE_CLASS_VARIABLE_READ_NODE: { - break; - } - case YP_NODE_CLASS_VARIABLE_WRITE_NODE: { - serialize_location(parser, &((yp_class_variable_write_node_t *)node)->name_loc, buffer); - if (((yp_class_variable_write_node_t *)node)->value == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_class_variable_write_node_t *)node)->value, buffer); - } - if (((yp_class_variable_write_node_t *)node)->operator_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_class_variable_write_node_t *)node)->operator_loc, buffer); - } - break; - } - case YP_NODE_CONSTANT_PATH_NODE: { - if (((yp_constant_path_node_t *)node)->parent == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_node_t *)node)->parent, buffer); - } - yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_node_t *)node)->child, buffer); - serialize_location(parser, &((yp_constant_path_node_t *)node)->delimiter_loc, buffer); - break; - } - case YP_NODE_CONSTANT_PATH_WRITE_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->target, buffer); - if (((yp_constant_path_write_node_t *)node)->operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_constant_path_write_node_t *)node)->operator, buffer); - } - if (((yp_constant_path_write_node_t *)node)->value == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_constant_path_write_node_t *)node)->value, buffer); - } - break; - } - case YP_NODE_CONSTANT_READ_NODE: { - break; - } - case YP_NODE_DEF_NODE: { - // serialize length - // encoding of location u32s make us need to save this offset. - size_t length_offset = buffer->length; - - yp_buffer_append_str(buffer, "\0\0\0\0", 4); /* consume 4 bytes, updated below */ - serialize_token(parser, &((yp_def_node_t *)node)->name, buffer); - if (((yp_def_node_t *)node)->receiver == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_def_node_t *)node)->receiver, buffer); - } - if (((yp_def_node_t *)node)->parameters == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_def_node_t *)node)->parameters, buffer); - } - if (((yp_def_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_def_node_t *)node)->statements, buffer); - } - uint32_t locals_size = yp_ulong_to_u32(((yp_def_node_t *)node)->locals.size); - yp_buffer_append_u32(buffer, locals_size); - for (uint32_t index = 0; index < locals_size; index++) { - serialize_token(parser, &((yp_def_node_t *)node)->locals.tokens[index], buffer); - } - serialize_location(parser, &((yp_def_node_t *)node)->def_keyword_loc, buffer); - if (((yp_def_node_t *)node)->operator_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_def_node_t *)node)->operator_loc, buffer); - } - if (((yp_def_node_t *)node)->lparen_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_def_node_t *)node)->lparen_loc, buffer); - } - if (((yp_def_node_t *)node)->rparen_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_def_node_t *)node)->rparen_loc, buffer); - } - if (((yp_def_node_t *)node)->equal_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_def_node_t *)node)->equal_loc, buffer); - } - if (((yp_def_node_t *)node)->end_keyword_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_def_node_t *)node)->end_keyword_loc, buffer); - } - // serialize length - uint32_t length = yp_ulong_to_u32(buffer->length - offset - sizeof(uint32_t)); - memcpy(buffer->value + length_offset, &length, sizeof(uint32_t)); - break; - } - case YP_NODE_DEFINED_NODE: { - if (((yp_defined_node_t *)node)->lparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_defined_node_t *)node)->lparen, buffer); - } - yp_serialize_node(parser, (yp_node_t *)((yp_defined_node_t *)node)->value, buffer); - if (((yp_defined_node_t *)node)->rparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_defined_node_t *)node)->rparen, buffer); - } - serialize_location(parser, &((yp_defined_node_t *)node)->keyword_loc, buffer); - break; - } - case YP_NODE_ELSE_NODE: { - serialize_token(parser, &((yp_else_node_t *)node)->else_keyword, buffer); - if (((yp_else_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_else_node_t *)node)->statements, buffer); - } - if (((yp_else_node_t *)node)->end_keyword.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_else_node_t *)node)->end_keyword, buffer); - } - break; - } - case YP_NODE_ENSURE_NODE: { - serialize_token(parser, &((yp_ensure_node_t *)node)->ensure_keyword, buffer); - if (((yp_ensure_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_ensure_node_t *)node)->statements, buffer); - } - serialize_token(parser, &((yp_ensure_node_t *)node)->end_keyword, buffer); - break; - } - case YP_NODE_FALSE_NODE: { - break; - } - case YP_NODE_FIND_PATTERN_NODE: { - if (((yp_find_pattern_node_t *)node)->constant == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->constant, buffer); - } - yp_serialize_node(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->left, buffer); - uint32_t requireds_size = yp_ulong_to_u32(((yp_find_pattern_node_t *)node)->requireds.size); - yp_buffer_append_u32(buffer, requireds_size); - for (uint32_t index = 0; index < requireds_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_find_pattern_node_t *)node)->requireds.nodes[index], buffer); - } - yp_serialize_node(parser, (yp_node_t *)((yp_find_pattern_node_t *)node)->right, buffer); - if (((yp_find_pattern_node_t *)node)->opening_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_find_pattern_node_t *)node)->opening_loc, buffer); - } - if (((yp_find_pattern_node_t *)node)->closing_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_find_pattern_node_t *)node)->closing_loc, buffer); - } - break; - } - case YP_NODE_FLOAT_NODE: { - break; - } - case YP_NODE_FOR_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_for_node_t *)node)->index, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_for_node_t *)node)->collection, buffer); - if (((yp_for_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_for_node_t *)node)->statements, buffer); - } - serialize_location(parser, &((yp_for_node_t *)node)->for_keyword_loc, buffer); - serialize_location(parser, &((yp_for_node_t *)node)->in_keyword_loc, buffer); - if (((yp_for_node_t *)node)->do_keyword_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_for_node_t *)node)->do_keyword_loc, buffer); - } - serialize_location(parser, &((yp_for_node_t *)node)->end_keyword_loc, buffer); - break; - } - case YP_NODE_FORWARDING_ARGUMENTS_NODE: { - break; - } - case YP_NODE_FORWARDING_PARAMETER_NODE: { - break; - } - case YP_NODE_FORWARDING_SUPER_NODE: { - if (((yp_forwarding_super_node_t *)node)->block == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_forwarding_super_node_t *)node)->block, buffer); - } - break; - } - case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { - serialize_token(parser, &((yp_global_variable_read_node_t *)node)->name, buffer); - break; - } - case YP_NODE_GLOBAL_VARIABLE_WRITE_NODE: { - serialize_token(parser, &((yp_global_variable_write_node_t *)node)->name, buffer); - if (((yp_global_variable_write_node_t *)node)->operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_global_variable_write_node_t *)node)->operator, buffer); - } - if (((yp_global_variable_write_node_t *)node)->value == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_global_variable_write_node_t *)node)->value, buffer); - } - break; - } - case YP_NODE_HASH_NODE: { - serialize_token(parser, &((yp_hash_node_t *)node)->opening, buffer); - uint32_t elements_size = yp_ulong_to_u32(((yp_hash_node_t *)node)->elements.size); - yp_buffer_append_u32(buffer, elements_size); - for (uint32_t index = 0; index < elements_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_hash_node_t *)node)->elements.nodes[index], buffer); - } - serialize_token(parser, &((yp_hash_node_t *)node)->closing, buffer); - break; - } - case YP_NODE_HASH_PATTERN_NODE: { - if (((yp_hash_pattern_node_t *)node)->constant == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->constant, buffer); - } - uint32_t assocs_size = yp_ulong_to_u32(((yp_hash_pattern_node_t *)node)->assocs.size); - yp_buffer_append_u32(buffer, assocs_size); - for (uint32_t index = 0; index < assocs_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_hash_pattern_node_t *)node)->assocs.nodes[index], buffer); - } - if (((yp_hash_pattern_node_t *)node)->kwrest == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_hash_pattern_node_t *)node)->kwrest, buffer); - } - if (((yp_hash_pattern_node_t *)node)->opening_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_hash_pattern_node_t *)node)->opening_loc, buffer); - } - if (((yp_hash_pattern_node_t *)node)->closing_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_hash_pattern_node_t *)node)->closing_loc, buffer); - } - break; - } - case YP_NODE_IF_NODE: { - serialize_token(parser, &((yp_if_node_t *)node)->if_keyword, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_if_node_t *)node)->predicate, buffer); - if (((yp_if_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_if_node_t *)node)->statements, buffer); - } - if (((yp_if_node_t *)node)->consequent == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_if_node_t *)node)->consequent, buffer); - } - if (((yp_if_node_t *)node)->end_keyword.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_if_node_t *)node)->end_keyword, buffer); - } - break; - } - case YP_NODE_IMAGINARY_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_imaginary_node_t *)node)->numeric, buffer); - break; - } - case YP_NODE_IN_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_in_node_t *)node)->pattern, buffer); - if (((yp_in_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_in_node_t *)node)->statements, buffer); - } - serialize_location(parser, &((yp_in_node_t *)node)->in_loc, buffer); - if (((yp_in_node_t *)node)->then_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_in_node_t *)node)->then_loc, buffer); - } - break; - } - case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { - break; - } - case YP_NODE_INSTANCE_VARIABLE_WRITE_NODE: { - serialize_location(parser, &((yp_instance_variable_write_node_t *)node)->name_loc, buffer); - if (((yp_instance_variable_write_node_t *)node)->value == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_instance_variable_write_node_t *)node)->value, buffer); - } - if (((yp_instance_variable_write_node_t *)node)->operator_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_instance_variable_write_node_t *)node)->operator_loc, buffer); - } - break; - } - case YP_NODE_INTEGER_NODE: { - break; - } - case YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE: { - serialize_token(parser, &((yp_interpolated_regular_expression_node_t *)node)->opening, buffer); - uint32_t parts_size = yp_ulong_to_u32(((yp_interpolated_regular_expression_node_t *)node)->parts.size); - yp_buffer_append_u32(buffer, parts_size); - for (uint32_t index = 0; index < parts_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_interpolated_regular_expression_node_t *)node)->parts.nodes[index], buffer); - } - serialize_token(parser, &((yp_interpolated_regular_expression_node_t *)node)->closing, buffer); - break; - } - case YP_NODE_INTERPOLATED_STRING_NODE: { - if (((yp_interpolated_string_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_interpolated_string_node_t *)node)->opening, buffer); - } - uint32_t parts_size = yp_ulong_to_u32(((yp_interpolated_string_node_t *)node)->parts.size); - yp_buffer_append_u32(buffer, parts_size); - for (uint32_t index = 0; index < parts_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_interpolated_string_node_t *)node)->parts.nodes[index], buffer); - } - if (((yp_interpolated_string_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_interpolated_string_node_t *)node)->closing, buffer); - } - break; - } - case YP_NODE_INTERPOLATED_SYMBOL_NODE: { - if (((yp_interpolated_symbol_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_interpolated_symbol_node_t *)node)->opening, buffer); - } - uint32_t parts_size = yp_ulong_to_u32(((yp_interpolated_symbol_node_t *)node)->parts.size); - yp_buffer_append_u32(buffer, parts_size); - for (uint32_t index = 0; index < parts_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_interpolated_symbol_node_t *)node)->parts.nodes[index], buffer); - } - if (((yp_interpolated_symbol_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_interpolated_symbol_node_t *)node)->closing, buffer); - } - break; - } - case YP_NODE_INTERPOLATED_X_STRING_NODE: { - serialize_token(parser, &((yp_interpolated_x_string_node_t *)node)->opening, buffer); - uint32_t parts_size = yp_ulong_to_u32(((yp_interpolated_x_string_node_t *)node)->parts.size); - yp_buffer_append_u32(buffer, parts_size); - for (uint32_t index = 0; index < parts_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_interpolated_x_string_node_t *)node)->parts.nodes[index], buffer); - } - serialize_token(parser, &((yp_interpolated_x_string_node_t *)node)->closing, buffer); - break; - } - case YP_NODE_KEYWORD_HASH_NODE: { - uint32_t elements_size = yp_ulong_to_u32(((yp_keyword_hash_node_t *)node)->elements.size); - yp_buffer_append_u32(buffer, elements_size); - for (uint32_t index = 0; index < elements_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_keyword_hash_node_t *)node)->elements.nodes[index], buffer); - } - break; - } - case YP_NODE_KEYWORD_PARAMETER_NODE: { - serialize_token(parser, &((yp_keyword_parameter_node_t *)node)->name, buffer); - if (((yp_keyword_parameter_node_t *)node)->value == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_keyword_parameter_node_t *)node)->value, buffer); - } - break; - } - case YP_NODE_KEYWORD_REST_PARAMETER_NODE: { - serialize_token(parser, &((yp_keyword_rest_parameter_node_t *)node)->operator, buffer); - if (((yp_keyword_rest_parameter_node_t *)node)->name.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_keyword_rest_parameter_node_t *)node)->name, buffer); - } - break; - } - case YP_NODE_LAMBDA_NODE: { - uint32_t locals_size = yp_ulong_to_u32(((yp_lambda_node_t *)node)->locals.size); - yp_buffer_append_u32(buffer, locals_size); - for (uint32_t index = 0; index < locals_size; index++) { - serialize_token(parser, &((yp_lambda_node_t *)node)->locals.tokens[index], buffer); - } - serialize_token(parser, &((yp_lambda_node_t *)node)->opening, buffer); - if (((yp_lambda_node_t *)node)->parameters == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_lambda_node_t *)node)->parameters, buffer); - } - if (((yp_lambda_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_lambda_node_t *)node)->statements, buffer); - } - break; - } - case YP_NODE_LOCAL_VARIABLE_READ_NODE: { - yp_buffer_append_u32(buffer, ((yp_local_variable_read_node_t *)node)->depth); - break; - } - case YP_NODE_LOCAL_VARIABLE_WRITE_NODE: { - serialize_location(parser, &((yp_local_variable_write_node_t *)node)->name_loc, buffer); - if (((yp_local_variable_write_node_t *)node)->value == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_local_variable_write_node_t *)node)->value, buffer); - } - if (((yp_local_variable_write_node_t *)node)->operator_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_local_variable_write_node_t *)node)->operator_loc, buffer); - } - yp_buffer_append_u32(buffer, ((yp_local_variable_write_node_t *)node)->depth); - break; - } - case YP_NODE_MATCH_PREDICATE_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->value, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_match_predicate_node_t *)node)->pattern, buffer); - serialize_location(parser, &((yp_match_predicate_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_MATCH_REQUIRED_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_match_required_node_t *)node)->value, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_match_required_node_t *)node)->pattern, buffer); - serialize_location(parser, &((yp_match_required_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_MISSING_NODE: { - break; - } - case YP_NODE_MODULE_NODE: { - uint32_t locals_size = yp_ulong_to_u32(((yp_module_node_t *)node)->locals.size); - yp_buffer_append_u32(buffer, locals_size); - for (uint32_t index = 0; index < locals_size; index++) { - serialize_token(parser, &((yp_module_node_t *)node)->locals.tokens[index], buffer); - } - serialize_token(parser, &((yp_module_node_t *)node)->module_keyword, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_module_node_t *)node)->constant_path, buffer); - if (((yp_module_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_module_node_t *)node)->statements, buffer); - } - serialize_token(parser, &((yp_module_node_t *)node)->end_keyword, buffer); - break; - } - case YP_NODE_MULTI_WRITE_NODE: { - uint32_t targets_size = yp_ulong_to_u32(((yp_multi_write_node_t *)node)->targets.size); - yp_buffer_append_u32(buffer, targets_size); - for (uint32_t index = 0; index < targets_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_multi_write_node_t *)node)->targets.nodes[index], buffer); - } - if (((yp_multi_write_node_t *)node)->operator.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_multi_write_node_t *)node)->operator, buffer); - } - if (((yp_multi_write_node_t *)node)->value == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_multi_write_node_t *)node)->value, buffer); - } - if (((yp_multi_write_node_t *)node)->lparen_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_multi_write_node_t *)node)->lparen_loc, buffer); - } - if (((yp_multi_write_node_t *)node)->rparen_loc.start == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_buffer_append_u8(buffer, 1); - serialize_location(parser, &((yp_multi_write_node_t *)node)->rparen_loc, buffer); - } - break; - } - case YP_NODE_NEXT_NODE: { - if (((yp_next_node_t *)node)->arguments == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_next_node_t *)node)->arguments, buffer); - } - serialize_location(parser, &((yp_next_node_t *)node)->keyword_loc, buffer); - break; - } - case YP_NODE_NIL_NODE: { - break; - } - case YP_NODE_NO_KEYWORDS_PARAMETER_NODE: { - serialize_location(parser, &((yp_no_keywords_parameter_node_t *)node)->operator_loc, buffer); - serialize_location(parser, &((yp_no_keywords_parameter_node_t *)node)->keyword_loc, buffer); - break; - } - case YP_NODE_OPERATOR_AND_ASSIGNMENT_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_operator_and_assignment_node_t *)node)->target, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_operator_and_assignment_node_t *)node)->value, buffer); - serialize_location(parser, &((yp_operator_and_assignment_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_OPERATOR_ASSIGNMENT_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_operator_assignment_node_t *)node)->target, buffer); - serialize_token(parser, &((yp_operator_assignment_node_t *)node)->operator, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_operator_assignment_node_t *)node)->value, buffer); - break; - } - case YP_NODE_OPERATOR_OR_ASSIGNMENT_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_operator_or_assignment_node_t *)node)->target, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_operator_or_assignment_node_t *)node)->value, buffer); - serialize_location(parser, &((yp_operator_or_assignment_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_OPTIONAL_PARAMETER_NODE: { - serialize_token(parser, &((yp_optional_parameter_node_t *)node)->name, buffer); - serialize_token(parser, &((yp_optional_parameter_node_t *)node)->equal_operator, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_optional_parameter_node_t *)node)->value, buffer); - break; - } - case YP_NODE_OR_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_or_node_t *)node)->left, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_or_node_t *)node)->right, buffer); - serialize_location(parser, &((yp_or_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_PARAMETERS_NODE: { - uint32_t requireds_size = yp_ulong_to_u32(((yp_parameters_node_t *)node)->requireds.size); - yp_buffer_append_u32(buffer, requireds_size); - for (uint32_t index = 0; index < requireds_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_parameters_node_t *)node)->requireds.nodes[index], buffer); - } - uint32_t optionals_size = yp_ulong_to_u32(((yp_parameters_node_t *)node)->optionals.size); - yp_buffer_append_u32(buffer, optionals_size); - for (uint32_t index = 0; index < optionals_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_parameters_node_t *)node)->optionals.nodes[index], buffer); - } - uint32_t posts_size = yp_ulong_to_u32(((yp_parameters_node_t *)node)->posts.size); - yp_buffer_append_u32(buffer, posts_size); - for (uint32_t index = 0; index < posts_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_parameters_node_t *)node)->posts.nodes[index], buffer); - } - if (((yp_parameters_node_t *)node)->rest == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_parameters_node_t *)node)->rest, buffer); - } - uint32_t keywords_size = yp_ulong_to_u32(((yp_parameters_node_t *)node)->keywords.size); - yp_buffer_append_u32(buffer, keywords_size); - for (uint32_t index = 0; index < keywords_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_parameters_node_t *)node)->keywords.nodes[index], buffer); - } - if (((yp_parameters_node_t *)node)->keyword_rest == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_parameters_node_t *)node)->keyword_rest, buffer); - } - if (((yp_parameters_node_t *)node)->block == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_parameters_node_t *)node)->block, buffer); - } - break; - } - case YP_NODE_PARENTHESES_NODE: { - if (((yp_parentheses_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_parentheses_node_t *)node)->statements, buffer); - } - serialize_location(parser, &((yp_parentheses_node_t *)node)->opening_loc, buffer); - serialize_location(parser, &((yp_parentheses_node_t *)node)->closing_loc, buffer); - break; - } - case YP_NODE_PINNED_EXPRESSION_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_pinned_expression_node_t *)node)->expression, buffer); - serialize_location(parser, &((yp_pinned_expression_node_t *)node)->operator_loc, buffer); - serialize_location(parser, &((yp_pinned_expression_node_t *)node)->lparen_loc, buffer); - serialize_location(parser, &((yp_pinned_expression_node_t *)node)->rparen_loc, buffer); - break; - } - case YP_NODE_PINNED_VARIABLE_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_pinned_variable_node_t *)node)->variable, buffer); - serialize_location(parser, &((yp_pinned_variable_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_POST_EXECUTION_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_post_execution_node_t *)node)->statements, buffer); - serialize_location(parser, &((yp_post_execution_node_t *)node)->keyword_loc, buffer); - serialize_location(parser, &((yp_post_execution_node_t *)node)->opening_loc, buffer); - serialize_location(parser, &((yp_post_execution_node_t *)node)->closing_loc, buffer); - break; - } - case YP_NODE_PRE_EXECUTION_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_pre_execution_node_t *)node)->statements, buffer); - serialize_location(parser, &((yp_pre_execution_node_t *)node)->keyword_loc, buffer); - serialize_location(parser, &((yp_pre_execution_node_t *)node)->opening_loc, buffer); - serialize_location(parser, &((yp_pre_execution_node_t *)node)->closing_loc, buffer); - break; - } - case YP_NODE_PROGRAM_NODE: { - uint32_t locals_size = yp_ulong_to_u32(((yp_program_node_t *)node)->locals.size); - yp_buffer_append_u32(buffer, locals_size); - for (uint32_t index = 0; index < locals_size; index++) { - serialize_token(parser, &((yp_program_node_t *)node)->locals.tokens[index], buffer); - } - yp_serialize_node(parser, (yp_node_t *)((yp_program_node_t *)node)->statements, buffer); - break; - } - case YP_NODE_RANGE_NODE: { - if (((yp_range_node_t *)node)->left == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_range_node_t *)node)->left, buffer); - } - if (((yp_range_node_t *)node)->right == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_range_node_t *)node)->right, buffer); - } - serialize_location(parser, &((yp_range_node_t *)node)->operator_loc, buffer); - break; - } - case YP_NODE_RATIONAL_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_rational_node_t *)node)->numeric, buffer); - break; - } - case YP_NODE_REDO_NODE: { - break; - } - case YP_NODE_REGULAR_EXPRESSION_NODE: { - serialize_token(parser, &((yp_regular_expression_node_t *)node)->opening, buffer); - serialize_token(parser, &((yp_regular_expression_node_t *)node)->content, buffer); - serialize_token(parser, &((yp_regular_expression_node_t *)node)->closing, buffer); - uint32_t unescaped_length = yp_ulong_to_u32(yp_string_length(&((yp_regular_expression_node_t *)node)->unescaped)); - yp_buffer_append_u32(buffer, unescaped_length); - yp_buffer_append_str(buffer, yp_string_source(&((yp_regular_expression_node_t *)node)->unescaped), unescaped_length); - break; - } - case YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE: { - uint32_t parameters_size = yp_ulong_to_u32(((yp_required_destructured_parameter_node_t *)node)->parameters.size); - yp_buffer_append_u32(buffer, parameters_size); - for (uint32_t index = 0; index < parameters_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_required_destructured_parameter_node_t *)node)->parameters.nodes[index], buffer); - } - serialize_token(parser, &((yp_required_destructured_parameter_node_t *)node)->opening, buffer); - serialize_token(parser, &((yp_required_destructured_parameter_node_t *)node)->closing, buffer); - break; - } - case YP_NODE_REQUIRED_PARAMETER_NODE: { - break; - } - case YP_NODE_RESCUE_MODIFIER_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->expression, buffer); - serialize_token(parser, &((yp_rescue_modifier_node_t *)node)->rescue_keyword, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_rescue_modifier_node_t *)node)->rescue_expression, buffer); - break; - } - case YP_NODE_RESCUE_NODE: { - serialize_token(parser, &((yp_rescue_node_t *)node)->rescue_keyword, buffer); - uint32_t exceptions_size = yp_ulong_to_u32(((yp_rescue_node_t *)node)->exceptions.size); - yp_buffer_append_u32(buffer, exceptions_size); - for (uint32_t index = 0; index < exceptions_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_rescue_node_t *)node)->exceptions.nodes[index], buffer); - } - if (((yp_rescue_node_t *)node)->equal_greater.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_rescue_node_t *)node)->equal_greater, buffer); - } - if (((yp_rescue_node_t *)node)->exception == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_rescue_node_t *)node)->exception, buffer); - } - if (((yp_rescue_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_rescue_node_t *)node)->statements, buffer); - } - if (((yp_rescue_node_t *)node)->consequent == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_rescue_node_t *)node)->consequent, buffer); - } - break; - } - case YP_NODE_REST_PARAMETER_NODE: { - serialize_token(parser, &((yp_rest_parameter_node_t *)node)->operator, buffer); - if (((yp_rest_parameter_node_t *)node)->name.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_rest_parameter_node_t *)node)->name, buffer); - } - break; - } - case YP_NODE_RETRY_NODE: { - break; - } - case YP_NODE_RETURN_NODE: { - serialize_token(parser, &((yp_return_node_t *)node)->keyword, buffer); - if (((yp_return_node_t *)node)->arguments == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_return_node_t *)node)->arguments, buffer); - } - break; - } - case YP_NODE_SELF_NODE: { - break; - } - case YP_NODE_SINGLETON_CLASS_NODE: { - uint32_t locals_size = yp_ulong_to_u32(((yp_singleton_class_node_t *)node)->locals.size); - yp_buffer_append_u32(buffer, locals_size); - for (uint32_t index = 0; index < locals_size; index++) { - serialize_token(parser, &((yp_singleton_class_node_t *)node)->locals.tokens[index], buffer); - } - serialize_token(parser, &((yp_singleton_class_node_t *)node)->class_keyword, buffer); - serialize_token(parser, &((yp_singleton_class_node_t *)node)->operator, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->expression, buffer); - if (((yp_singleton_class_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_singleton_class_node_t *)node)->statements, buffer); - } - serialize_token(parser, &((yp_singleton_class_node_t *)node)->end_keyword, buffer); - break; - } - case YP_NODE_SOURCE_ENCODING_NODE: { - break; - } - case YP_NODE_SOURCE_FILE_NODE: { - uint32_t filepath_length = yp_ulong_to_u32(yp_string_length(&((yp_source_file_node_t *)node)->filepath)); - yp_buffer_append_u32(buffer, filepath_length); - yp_buffer_append_str(buffer, yp_string_source(&((yp_source_file_node_t *)node)->filepath), filepath_length); - break; - } - case YP_NODE_SOURCE_LINE_NODE: { - break; - } - case YP_NODE_SPLAT_NODE: { - serialize_token(parser, &((yp_splat_node_t *)node)->operator, buffer); - if (((yp_splat_node_t *)node)->expression == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_splat_node_t *)node)->expression, buffer); - } - break; - } - case YP_NODE_STATEMENTS_NODE: { - uint32_t body_size = yp_ulong_to_u32(((yp_statements_node_t *)node)->body.size); - yp_buffer_append_u32(buffer, body_size); - for (uint32_t index = 0; index < body_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_statements_node_t *)node)->body.nodes[index], buffer); - } - break; - } - case YP_NODE_STRING_CONCAT_NODE: { - yp_serialize_node(parser, (yp_node_t *)((yp_string_concat_node_t *)node)->left, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_string_concat_node_t *)node)->right, buffer); - break; - } - case YP_NODE_STRING_INTERPOLATED_NODE: { - serialize_token(parser, &((yp_string_interpolated_node_t *)node)->opening, buffer); - if (((yp_string_interpolated_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_string_interpolated_node_t *)node)->statements, buffer); - } - serialize_token(parser, &((yp_string_interpolated_node_t *)node)->closing, buffer); - break; - } - case YP_NODE_STRING_NODE: { - if (((yp_string_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_string_node_t *)node)->opening, buffer); - } - serialize_token(parser, &((yp_string_node_t *)node)->content, buffer); - if (((yp_string_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_string_node_t *)node)->closing, buffer); - } - uint32_t unescaped_length = yp_ulong_to_u32(yp_string_length(&((yp_string_node_t *)node)->unescaped)); - yp_buffer_append_u32(buffer, unescaped_length); - yp_buffer_append_str(buffer, yp_string_source(&((yp_string_node_t *)node)->unescaped), unescaped_length); - break; - } - case YP_NODE_SUPER_NODE: { - serialize_token(parser, &((yp_super_node_t *)node)->keyword, buffer); - if (((yp_super_node_t *)node)->lparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_super_node_t *)node)->lparen, buffer); - } - if (((yp_super_node_t *)node)->arguments == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_super_node_t *)node)->arguments, buffer); - } - if (((yp_super_node_t *)node)->rparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_super_node_t *)node)->rparen, buffer); - } - if (((yp_super_node_t *)node)->block == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_super_node_t *)node)->block, buffer); - } - break; - } - case YP_NODE_SYMBOL_NODE: { - if (((yp_symbol_node_t *)node)->opening.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_symbol_node_t *)node)->opening, buffer); - } - serialize_token(parser, &((yp_symbol_node_t *)node)->value, buffer); - if (((yp_symbol_node_t *)node)->closing.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_symbol_node_t *)node)->closing, buffer); - } - uint32_t unescaped_length = yp_ulong_to_u32(yp_string_length(&((yp_symbol_node_t *)node)->unescaped)); - yp_buffer_append_u32(buffer, unescaped_length); - yp_buffer_append_str(buffer, yp_string_source(&((yp_symbol_node_t *)node)->unescaped), unescaped_length); - break; - } - case YP_NODE_TRUE_NODE: { - break; - } - case YP_NODE_UNDEF_NODE: { - uint32_t names_size = yp_ulong_to_u32(((yp_undef_node_t *)node)->names.size); - yp_buffer_append_u32(buffer, names_size); - for (uint32_t index = 0; index < names_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_undef_node_t *)node)->names.nodes[index], buffer); - } - serialize_location(parser, &((yp_undef_node_t *)node)->keyword_loc, buffer); - break; - } - case YP_NODE_UNLESS_NODE: { - serialize_token(parser, &((yp_unless_node_t *)node)->keyword, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_unless_node_t *)node)->predicate, buffer); - if (((yp_unless_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_unless_node_t *)node)->statements, buffer); - } - if (((yp_unless_node_t *)node)->consequent == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_unless_node_t *)node)->consequent, buffer); - } - if (((yp_unless_node_t *)node)->end_keyword.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_unless_node_t *)node)->end_keyword, buffer); - } - break; - } - case YP_NODE_UNTIL_NODE: { - serialize_token(parser, &((yp_until_node_t *)node)->keyword, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_until_node_t *)node)->predicate, buffer); - if (((yp_until_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_until_node_t *)node)->statements, buffer); - } - break; - } - case YP_NODE_WHEN_NODE: { - serialize_token(parser, &((yp_when_node_t *)node)->when_keyword, buffer); - uint32_t conditions_size = yp_ulong_to_u32(((yp_when_node_t *)node)->conditions.size); - yp_buffer_append_u32(buffer, conditions_size); - for (uint32_t index = 0; index < conditions_size; index++) { - yp_serialize_node(parser, (yp_node_t *) ((yp_when_node_t *)node)->conditions.nodes[index], buffer); - } - if (((yp_when_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_when_node_t *)node)->statements, buffer); - } - break; - } - case YP_NODE_WHILE_NODE: { - serialize_token(parser, &((yp_while_node_t *)node)->keyword, buffer); - yp_serialize_node(parser, (yp_node_t *)((yp_while_node_t *)node)->predicate, buffer); - if (((yp_while_node_t *)node)->statements == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_while_node_t *)node)->statements, buffer); - } - break; - } - case YP_NODE_X_STRING_NODE: { - serialize_token(parser, &((yp_x_string_node_t *)node)->opening, buffer); - serialize_token(parser, &((yp_x_string_node_t *)node)->content, buffer); - serialize_token(parser, &((yp_x_string_node_t *)node)->closing, buffer); - uint32_t unescaped_length = yp_ulong_to_u32(yp_string_length(&((yp_x_string_node_t *)node)->unescaped)); - yp_buffer_append_u32(buffer, unescaped_length); - yp_buffer_append_str(buffer, yp_string_source(&((yp_x_string_node_t *)node)->unescaped), unescaped_length); - break; - } - case YP_NODE_YIELD_NODE: { - serialize_token(parser, &((yp_yield_node_t *)node)->keyword, buffer); - if (((yp_yield_node_t *)node)->lparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_yield_node_t *)node)->lparen, buffer); - } - if (((yp_yield_node_t *)node)->arguments == NULL) { - yp_buffer_append_u8(buffer, 0); - } else { - yp_serialize_node(parser, (yp_node_t *)((yp_yield_node_t *)node)->arguments, buffer); - } - if (((yp_yield_node_t *)node)->rparen.type == YP_TOKEN_NOT_PROVIDED) { - yp_buffer_append_u8(buffer, 0); - } else { - serialize_token(parser, &((yp_yield_node_t *)node)->rparen, buffer); - } - break; - } - } -} diff --git a/src/main/c/yarp/src/yarp/src/token_type.c b/src/main/c/yarp/src/yarp/src/token_type.c deleted file mode 100644 index edc96fb7e854..000000000000 --- a/src/main/c/yarp/src/yarp/src/token_type.c +++ /dev/null @@ -1,337 +0,0 @@ -/******************************************************************************/ -/* This file is generated by the bin/template script and should not be */ -/* modified manually. See */ -/* bin/templates/src/token_type.c.erb */ -/* if you are looking to modify the */ -/* template */ -/******************************************************************************/ -#include - -#include "yarp/ast.h" - -// Returns a string representation of the given token type. -__attribute__((__visibility__("default"))) const char * -yp_token_type_to_str(yp_token_type_t token_type) -{ - switch (token_type) { - case YP_TOKEN_EOF: - return "EOF"; - case YP_TOKEN_MISSING: - return "MISSING"; - case YP_TOKEN_NOT_PROVIDED: - return "NOT_PROVIDED"; - case YP_TOKEN_AMPERSAND: - return "AMPERSAND"; - case YP_TOKEN_AMPERSAND_AMPERSAND: - return "AMPERSAND_AMPERSAND"; - case YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL: - return "AMPERSAND_AMPERSAND_EQUAL"; - case YP_TOKEN_AMPERSAND_DOT: - return "AMPERSAND_DOT"; - case YP_TOKEN_AMPERSAND_EQUAL: - return "AMPERSAND_EQUAL"; - case YP_TOKEN_BACKTICK: - return "BACKTICK"; - case YP_TOKEN_BACK_REFERENCE: - return "BACK_REFERENCE"; - case YP_TOKEN_BANG: - return "BANG"; - case YP_TOKEN_BANG_EQUAL: - return "BANG_EQUAL"; - case YP_TOKEN_BANG_TILDE: - return "BANG_TILDE"; - case YP_TOKEN_BRACE_LEFT: - return "BRACE_LEFT"; - case YP_TOKEN_BRACE_RIGHT: - return "BRACE_RIGHT"; - case YP_TOKEN_BRACKET_LEFT: - return "BRACKET_LEFT"; - case YP_TOKEN_BRACKET_LEFT_ARRAY: - return "BRACKET_LEFT_ARRAY"; - case YP_TOKEN_BRACKET_LEFT_RIGHT: - return "BRACKET_LEFT_RIGHT"; - case YP_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: - return "BRACKET_LEFT_RIGHT_EQUAL"; - case YP_TOKEN_BRACKET_RIGHT: - return "BRACKET_RIGHT"; - case YP_TOKEN_CARET: - return "CARET"; - case YP_TOKEN_CARET_EQUAL: - return "CARET_EQUAL"; - case YP_TOKEN_CHARACTER_LITERAL: - return "CHARACTER_LITERAL"; - case YP_TOKEN_CLASS_VARIABLE: - return "CLASS_VARIABLE"; - case YP_TOKEN_COLON: - return "COLON"; - case YP_TOKEN_COLON_COLON: - return "COLON_COLON"; - case YP_TOKEN_COMMA: - return "COMMA"; - case YP_TOKEN_COMMENT: - return "COMMENT"; - case YP_TOKEN_CONSTANT: - return "CONSTANT"; - case YP_TOKEN_DOT: - return "DOT"; - case YP_TOKEN_DOT_DOT: - return "DOT_DOT"; - case YP_TOKEN_DOT_DOT_DOT: - return "DOT_DOT_DOT"; - case YP_TOKEN_EMBDOC_BEGIN: - return "EMBDOC_BEGIN"; - case YP_TOKEN_EMBDOC_END: - return "EMBDOC_END"; - case YP_TOKEN_EMBDOC_LINE: - return "EMBDOC_LINE"; - case YP_TOKEN_EMBEXPR_BEGIN: - return "EMBEXPR_BEGIN"; - case YP_TOKEN_EMBEXPR_END: - return "EMBEXPR_END"; - case YP_TOKEN_EMBVAR: - return "EMBVAR"; - case YP_TOKEN_EQUAL: - return "EQUAL"; - case YP_TOKEN_EQUAL_EQUAL: - return "EQUAL_EQUAL"; - case YP_TOKEN_EQUAL_EQUAL_EQUAL: - return "EQUAL_EQUAL_EQUAL"; - case YP_TOKEN_EQUAL_GREATER: - return "EQUAL_GREATER"; - case YP_TOKEN_EQUAL_TILDE: - return "EQUAL_TILDE"; - case YP_TOKEN_FLOAT: - return "FLOAT"; - case YP_TOKEN_GLOBAL_VARIABLE: - return "GLOBAL_VARIABLE"; - case YP_TOKEN_GREATER: - return "GREATER"; - case YP_TOKEN_GREATER_EQUAL: - return "GREATER_EQUAL"; - case YP_TOKEN_GREATER_GREATER: - return "GREATER_GREATER"; - case YP_TOKEN_GREATER_GREATER_EQUAL: - return "GREATER_GREATER_EQUAL"; - case YP_TOKEN_HEREDOC_END: - return "HEREDOC_END"; - case YP_TOKEN_HEREDOC_START: - return "HEREDOC_START"; - case YP_TOKEN_IDENTIFIER: - return "IDENTIFIER"; - case YP_TOKEN_IGNORED_NEWLINE: - return "IGNORED_NEWLINE"; - case YP_TOKEN_IMAGINARY_NUMBER: - return "IMAGINARY_NUMBER"; - case YP_TOKEN_INSTANCE_VARIABLE: - return "INSTANCE_VARIABLE"; - case YP_TOKEN_INTEGER: - return "INTEGER"; - case YP_TOKEN_KEYWORD_ALIAS: - return "KEYWORD_ALIAS"; - case YP_TOKEN_KEYWORD_AND: - return "KEYWORD_AND"; - case YP_TOKEN_KEYWORD_BEGIN: - return "KEYWORD_BEGIN"; - case YP_TOKEN_KEYWORD_BEGIN_UPCASE: - return "KEYWORD_BEGIN_UPCASE"; - case YP_TOKEN_KEYWORD_BREAK: - return "KEYWORD_BREAK"; - case YP_TOKEN_KEYWORD_CASE: - return "KEYWORD_CASE"; - case YP_TOKEN_KEYWORD_CLASS: - return "KEYWORD_CLASS"; - case YP_TOKEN_KEYWORD_DEF: - return "KEYWORD_DEF"; - case YP_TOKEN_KEYWORD_DEFINED: - return "KEYWORD_DEFINED"; - case YP_TOKEN_KEYWORD_DO: - return "KEYWORD_DO"; - case YP_TOKEN_KEYWORD_DO_LOOP: - return "KEYWORD_DO_LOOP"; - case YP_TOKEN_KEYWORD_ELSE: - return "KEYWORD_ELSE"; - case YP_TOKEN_KEYWORD_ELSIF: - return "KEYWORD_ELSIF"; - case YP_TOKEN_KEYWORD_END: - return "KEYWORD_END"; - case YP_TOKEN_KEYWORD_END_UPCASE: - return "KEYWORD_END_UPCASE"; - case YP_TOKEN_KEYWORD_ENSURE: - return "KEYWORD_ENSURE"; - case YP_TOKEN_KEYWORD_FALSE: - return "KEYWORD_FALSE"; - case YP_TOKEN_KEYWORD_FOR: - return "KEYWORD_FOR"; - case YP_TOKEN_KEYWORD_IF: - return "KEYWORD_IF"; - case YP_TOKEN_KEYWORD_IF_MODIFIER: - return "KEYWORD_IF_MODIFIER"; - case YP_TOKEN_KEYWORD_IN: - return "KEYWORD_IN"; - case YP_TOKEN_KEYWORD_MODULE: - return "KEYWORD_MODULE"; - case YP_TOKEN_KEYWORD_NEXT: - return "KEYWORD_NEXT"; - case YP_TOKEN_KEYWORD_NIL: - return "KEYWORD_NIL"; - case YP_TOKEN_KEYWORD_NOT: - return "KEYWORD_NOT"; - case YP_TOKEN_KEYWORD_OR: - return "KEYWORD_OR"; - case YP_TOKEN_KEYWORD_REDO: - return "KEYWORD_REDO"; - case YP_TOKEN_KEYWORD_RESCUE: - return "KEYWORD_RESCUE"; - case YP_TOKEN_KEYWORD_RESCUE_MODIFIER: - return "KEYWORD_RESCUE_MODIFIER"; - case YP_TOKEN_KEYWORD_RETRY: - return "KEYWORD_RETRY"; - case YP_TOKEN_KEYWORD_RETURN: - return "KEYWORD_RETURN"; - case YP_TOKEN_KEYWORD_SELF: - return "KEYWORD_SELF"; - case YP_TOKEN_KEYWORD_SUPER: - return "KEYWORD_SUPER"; - case YP_TOKEN_KEYWORD_THEN: - return "KEYWORD_THEN"; - case YP_TOKEN_KEYWORD_TRUE: - return "KEYWORD_TRUE"; - case YP_TOKEN_KEYWORD_UNDEF: - return "KEYWORD_UNDEF"; - case YP_TOKEN_KEYWORD_UNLESS: - return "KEYWORD_UNLESS"; - case YP_TOKEN_KEYWORD_UNLESS_MODIFIER: - return "KEYWORD_UNLESS_MODIFIER"; - case YP_TOKEN_KEYWORD_UNTIL: - return "KEYWORD_UNTIL"; - case YP_TOKEN_KEYWORD_UNTIL_MODIFIER: - return "KEYWORD_UNTIL_MODIFIER"; - case YP_TOKEN_KEYWORD_WHEN: - return "KEYWORD_WHEN"; - case YP_TOKEN_KEYWORD_WHILE: - return "KEYWORD_WHILE"; - case YP_TOKEN_KEYWORD_WHILE_MODIFIER: - return "KEYWORD_WHILE_MODIFIER"; - case YP_TOKEN_KEYWORD_YIELD: - return "KEYWORD_YIELD"; - case YP_TOKEN_KEYWORD___ENCODING__: - return "KEYWORD___ENCODING__"; - case YP_TOKEN_KEYWORD___FILE__: - return "KEYWORD___FILE__"; - case YP_TOKEN_KEYWORD___LINE__: - return "KEYWORD___LINE__"; - case YP_TOKEN_LABEL: - return "LABEL"; - case YP_TOKEN_LABEL_END: - return "LABEL_END"; - case YP_TOKEN_LAMBDA_BEGIN: - return "LAMBDA_BEGIN"; - case YP_TOKEN_LESS: - return "LESS"; - case YP_TOKEN_LESS_EQUAL: - return "LESS_EQUAL"; - case YP_TOKEN_LESS_EQUAL_GREATER: - return "LESS_EQUAL_GREATER"; - case YP_TOKEN_LESS_LESS: - return "LESS_LESS"; - case YP_TOKEN_LESS_LESS_EQUAL: - return "LESS_LESS_EQUAL"; - case YP_TOKEN_MINUS: - return "MINUS"; - case YP_TOKEN_MINUS_EQUAL: - return "MINUS_EQUAL"; - case YP_TOKEN_MINUS_GREATER: - return "MINUS_GREATER"; - case YP_TOKEN_NEWLINE: - return "NEWLINE"; - case YP_TOKEN_NTH_REFERENCE: - return "NTH_REFERENCE"; - case YP_TOKEN_PARENTHESIS_LEFT: - return "PARENTHESIS_LEFT"; - case YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES: - return "PARENTHESIS_LEFT_PARENTHESES"; - case YP_TOKEN_PARENTHESIS_RIGHT: - return "PARENTHESIS_RIGHT"; - case YP_TOKEN_PERCENT: - return "PERCENT"; - case YP_TOKEN_PERCENT_EQUAL: - return "PERCENT_EQUAL"; - case YP_TOKEN_PERCENT_LOWER_I: - return "PERCENT_LOWER_I"; - case YP_TOKEN_PERCENT_LOWER_W: - return "PERCENT_LOWER_W"; - case YP_TOKEN_PERCENT_LOWER_X: - return "PERCENT_LOWER_X"; - case YP_TOKEN_PERCENT_UPPER_I: - return "PERCENT_UPPER_I"; - case YP_TOKEN_PERCENT_UPPER_W: - return "PERCENT_UPPER_W"; - case YP_TOKEN_PIPE: - return "PIPE"; - case YP_TOKEN_PIPE_EQUAL: - return "PIPE_EQUAL"; - case YP_TOKEN_PIPE_PIPE: - return "PIPE_PIPE"; - case YP_TOKEN_PIPE_PIPE_EQUAL: - return "PIPE_PIPE_EQUAL"; - case YP_TOKEN_PLUS: - return "PLUS"; - case YP_TOKEN_PLUS_EQUAL: - return "PLUS_EQUAL"; - case YP_TOKEN_QUESTION_MARK: - return "QUESTION_MARK"; - case YP_TOKEN_RATIONAL_NUMBER: - return "RATIONAL_NUMBER"; - case YP_TOKEN_REGEXP_BEGIN: - return "REGEXP_BEGIN"; - case YP_TOKEN_REGEXP_END: - return "REGEXP_END"; - case YP_TOKEN_SEMICOLON: - return "SEMICOLON"; - case YP_TOKEN_SLASH: - return "SLASH"; - case YP_TOKEN_SLASH_EQUAL: - return "SLASH_EQUAL"; - case YP_TOKEN_STAR: - return "STAR"; - case YP_TOKEN_STAR_EQUAL: - return "STAR_EQUAL"; - case YP_TOKEN_STAR_STAR: - return "STAR_STAR"; - case YP_TOKEN_STAR_STAR_EQUAL: - return "STAR_STAR_EQUAL"; - case YP_TOKEN_STRING_BEGIN: - return "STRING_BEGIN"; - case YP_TOKEN_STRING_CONTENT: - return "STRING_CONTENT"; - case YP_TOKEN_STRING_END: - return "STRING_END"; - case YP_TOKEN_SYMBOL_BEGIN: - return "SYMBOL_BEGIN"; - case YP_TOKEN_TILDE: - return "TILDE"; - case YP_TOKEN_UCOLON_COLON: - return "UCOLON_COLON"; - case YP_TOKEN_UDOT_DOT: - return "UDOT_DOT"; - case YP_TOKEN_UDOT_DOT_DOT: - return "UDOT_DOT_DOT"; - case YP_TOKEN_UMINUS: - return "UMINUS"; - case YP_TOKEN_UMINUS_NUM: - return "UMINUS_NUM"; - case YP_TOKEN_UPLUS: - return "UPLUS"; - case YP_TOKEN_USTAR: - return "USTAR"; - case YP_TOKEN_USTAR_STAR: - return "USTAR_STAR"; - case YP_TOKEN_WORDS_SEP: - return "WORDS_SEP"; - case YP_TOKEN___END__: - return "__END__"; - case YP_TOKEN_MAXIMUM: - return "MAXIMUM"; - } - return "\0"; -} diff --git a/src/main/c/yarp/src/yarp/src/unescape.c b/src/main/c/yarp/src/yarp/src/unescape.c deleted file mode 100644 index 9501c8cdb4ca..000000000000 --- a/src/main/c/yarp/src/yarp/src/unescape.c +++ /dev/null @@ -1,551 +0,0 @@ -#include "yarp/unescape.h" - -/******************************************************************************/ -/* Character checks */ -/******************************************************************************/ - -static inline bool -yp_char_is_hexadecimal_digits(const char *c, size_t length) { - for (size_t index = 0; index < length; index++) { - if (!yp_char_is_hexadecimal_digit(c[index])) { - return false; - } - } - return true; -} - -/******************************************************************************/ -/* Lookup tables for characters */ -/******************************************************************************/ - -// This is a lookup table for unescapes that only take up a single character. -static const unsigned char unescape_chars[] = { - ['\''] = '\'', - ['\\'] = '\\', - ['a'] = '\a', - ['b'] = '\b', - ['e'] = '\033', - ['f'] = '\f', - ['n'] = '\n', - ['r'] = '\r', - ['s'] = ' ', - ['t'] = '\t', - ['v'] = '\v' -}; - -// This is a lookup table for whether or not an ASCII character is printable. -static const bool ascii_printable_chars[] = { - 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 1, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, - 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 0 -}; - -static inline bool -char_is_ascii_printable(const char c) { - return ascii_printable_chars[(unsigned char) c]; -} - -/******************************************************************************/ -/* Unescaping for segments */ -/******************************************************************************/ - -// Scan the 1-3 digits of octal into the value. Returns the number of digits -// scanned. -static inline size_t -unescape_octal(const char *backslash, unsigned char *value) { - *value = (unsigned char) (backslash[1] - '0'); - if (!yp_char_is_octal_digit(backslash[2])) { - return 2; - } - - *value = (*value << 3) | (backslash[2] - '0'); - if (!yp_char_is_octal_digit(backslash[3])) { - return 3; - } - - *value = (*value << 3) | (backslash[3] - '0'); - return 4; -} - -// Convert a hexadecimal digit into its equivalent value. -static inline unsigned char -unescape_hexadecimal_digit(const char value) { - return (value <= '9') ? (unsigned char) (value - '0') : (value & 0x7) + 9; -} - -// Scan the 1-2 digits of hexadecimal into the value. Returns the number of -// digits scanned. -static inline size_t -unescape_hexadecimal(const char *backslash, unsigned char *value) { - *value = unescape_hexadecimal_digit(backslash[2]); - if (!yp_char_is_hexadecimal_digit(backslash[3])) { - return 3; - } - - *value = (*value << 4) | unescape_hexadecimal_digit(backslash[3]); - return 4; -} - -// Scan the 4 digits of a Unicode escape into the value. Returns the number of -// digits scanned. This function assumes that the characters have already been -// validated. -static inline void -unescape_unicode(const char *string, size_t length, uint32_t *value) { - *value = 0; - for (size_t index = 0; index < length; index++) { - if (index != 0) *value <<= 4; - *value |= unescape_hexadecimal_digit(string[index]); - } -} - -// Accepts the pointer to the string to write the unicode value along with the -// 32-bit value to write. Writes the UTF-8 representation of the value to the -// string and returns the number of bytes written. -static inline size_t -unescape_unicode_write(char *dest, uint32_t value, const char *start, const char *end, yp_list_t *error_list) { - unsigned char *bytes = (unsigned char *) dest; - - if (value <= 0x7F) { - // 0xxxxxxx - bytes[0] = value; - return 1; - } - - if (value <= 0x7FF) { - // 110xxxxx 10xxxxxx - bytes[0] = 0xC0 | (value >> 6); - bytes[1] = 0x80 | (value & 0x3F); - return 2; - } - - if (value <= 0xFFFF) { - // 1110xxxx 10xxxxxx 10xxxxxx - bytes[0] = 0xE0 | (value >> 12); - bytes[1] = 0x80 | ((value >> 6) & 0x3F); - bytes[2] = 0x80 | (value & 0x3F); - return 3; - } - - // At this point it must be a 4 digit UTF-8 representation. If it's not, then - // the input is invalid. - if (value <= 0x10FFFF) { - // 11110xxx 10xxxxxx 10xxxxxx 10xxxxxx - bytes[0] = 0xF0 | (value >> 18); - bytes[1] = 0x80 | ((value >> 12) & 0x3F); - bytes[2] = 0x80 | ((value >> 6) & 0x3F); - bytes[3] = 0x80 | (value & 0x3F); - return 4; - } - - // If we get here, then the value is too big. This is an error, but we don't - // want to just crash, so instead we'll add an error to the error list and put - // in a replacement character instead. - yp_diagnostic_list_append(error_list, start, end, "Invalid Unicode escape sequence."); - bytes[0] = 0xEF; - bytes[1] = 0xBF; - bytes[2] = 0xBD; - return 3; -} - -typedef enum { - YP_UNESCAPE_FLAG_NONE = 0, - YP_UNESCAPE_FLAG_CONTROL = 1, - YP_UNESCAPE_FLAG_META = 2, - YP_UNESCAPE_FLAG_EXPECT_SINGLE = 4 -} yp_unescape_flag_t; - -// Unescape a single character value based on the given flags. -static inline unsigned char -unescape_char(const unsigned char value, const unsigned char flags) { - unsigned char unescaped = value; - - if (flags & YP_UNESCAPE_FLAG_CONTROL) { - unescaped &= 0x1f; - } - - if (flags & YP_UNESCAPE_FLAG_META) { - unescaped |= 0x80; - } - - return unescaped; -} - -// Read a specific escape sequence into the given destination. -static const char * -unescape(char *dest, size_t *dest_length, const char *backslash, const char *end, yp_list_t *error_list, const unsigned char flags, bool write_to_str) { - switch (backslash[1]) { - // \a \b \e \f \n \r \s \t \v - case '\r': { - // if this is an \r\n we need to escape both - if (write_to_str) { - dest[(*dest_length)++] = (char) unescape_char(unescape_chars[(unsigned char) backslash[1]], flags); - } - - if (backslash[2] == '\n') { - if (write_to_str) { - dest[(*dest_length)++] = (char) unescape_char(unescape_chars[(unsigned char) backslash[1]], flags); - } - return backslash + 3; - } - return backslash + 2; - } - case 'a': - case 'b': - case 'e': - case 'f': - case 'n': - case 'r': - case 's': - case 't': - case 'v': - if (write_to_str) { - dest[(*dest_length)++] = (char) unescape_char(unescape_chars[(unsigned char) backslash[1]], flags); - } - return backslash + 2; - // \nnn octal bit pattern, where nnn is 1-3 octal digits ([0-7]) - case '0': case '1': case '2': case '3': case '4': - case '5': case '6': case '7': case '8': case '9': { - unsigned char value; - const char *cursor = backslash + unescape_octal(backslash, &value); - - if (write_to_str) { - dest[(*dest_length)++] = (char) unescape_char(value, flags); - } - return cursor; - } - // \xnn hexadecimal bit pattern, where nn is 1-2 hexadecimal digits ([0-9a-fA-F]) - case 'x': { - unsigned char value; - const char *cursor = backslash + unescape_hexadecimal(backslash, &value); - - if (write_to_str) { - dest[(*dest_length)++] = (char) unescape_char(value, flags); - } - return cursor; - } - // \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F]) - // \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F]) - case 'u': { - if ((flags & YP_UNESCAPE_FLAG_CONTROL) | (flags & YP_UNESCAPE_FLAG_META)) { - yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Unicode escape sequence cannot be used with control or meta flags."); - return backslash + 2; - } - - if ((backslash + 3) < end && backslash[2] == '{') { - const char *unicode_cursor = backslash + 3; - const char *extra_codepoints_start = NULL; - int codepoints_count = 0; - - unicode_cursor += yp_strspn_whitespace(unicode_cursor, end - unicode_cursor); - - while ((*unicode_cursor != '}') && (unicode_cursor < end)) { - const char *unicode_start = unicode_cursor; - size_t hexadecimal_length = yp_strspn_hexadecimal_digit(unicode_cursor, end - unicode_cursor); - - // \u{nnnn} character literal allows only 1-6 hexadecimal digits - if (hexadecimal_length > 6) - yp_diagnostic_list_append(error_list, unicode_cursor, unicode_cursor + hexadecimal_length, "invalid Unicode escape."); - - // there are not hexadecimal characters - if (hexadecimal_length == 0) { - yp_diagnostic_list_append(error_list, unicode_cursor, unicode_cursor + hexadecimal_length, "unterminated Unicode escape"); - return unicode_cursor; - } - - unicode_cursor += hexadecimal_length; - - codepoints_count++; - if (flags & YP_UNESCAPE_FLAG_EXPECT_SINGLE && codepoints_count == 2) - extra_codepoints_start = unicode_start; - - uint32_t value; - unescape_unicode(unicode_start, (size_t) (unicode_cursor - unicode_start), &value); - if (write_to_str) { - *dest_length += unescape_unicode_write(dest + *dest_length, value, unicode_start, unicode_cursor, error_list); - } - - unicode_cursor += yp_strspn_whitespace(unicode_cursor, end - unicode_cursor); - } - - // ?\u{nnnn} character literal should contain only one codepoint and cannot be like ?\u{nnnn mmmm} - if (flags & YP_UNESCAPE_FLAG_EXPECT_SINGLE && codepoints_count > 1) - yp_diagnostic_list_append(error_list, extra_codepoints_start, unicode_cursor - 1, "Multiple codepoints at single character literal"); - - return unicode_cursor + 1; - } - - if ((backslash + 2) < end && yp_char_is_hexadecimal_digits(backslash + 2, 4)) { - uint32_t value; - unescape_unicode(backslash + 2, 4, &value); - - if (write_to_str) { - *dest_length += unescape_unicode_write(dest + *dest_length, value, backslash + 2, backslash + 6, error_list); - } - return backslash + 6; - } - - yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Invalid Unicode escape sequence"); - return backslash + 2; - } - // \c\M-x meta control character, where x is an ASCII printable character - // \c? delete, ASCII 7Fh (DEL) - // \cx control character, where x is an ASCII printable character - case 'c': - if (backslash + 2 >= end) { - yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Invalid control escape sequence"); - return end; - } - - if (flags & YP_UNESCAPE_FLAG_CONTROL) { - yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Control escape sequence cannot be doubled."); - return backslash + 2; - } - - switch (backslash[2]) { - case '\\': - return unescape(dest, dest_length, backslash + 2, end, error_list, flags | YP_UNESCAPE_FLAG_CONTROL, write_to_str); - case '?': - if (write_to_str) { - dest[(*dest_length)++] = (char) unescape_char(0x7f, flags); - } - return backslash + 3; - default: { - if (!char_is_ascii_printable(backslash[2])) { - yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Invalid control escape sequence"); - return backslash + 2; - } - - if (write_to_str) { - dest[(*dest_length)++] = (char) unescape_char((const unsigned char) backslash[2], flags | YP_UNESCAPE_FLAG_CONTROL); - } - return backslash + 3; - } - } - // \C-x control character, where x is an ASCII printable character - // \C-? delete, ASCII 7Fh (DEL) - case 'C': - if (backslash + 3 >= end) { - yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Invalid control escape sequence"); - return end; - } - - if (flags & YP_UNESCAPE_FLAG_CONTROL) { - yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Control escape sequence cannot be doubled."); - return backslash + 2; - } - - if (backslash[2] != '-') { - yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Invalid control escape sequence"); - return backslash + 2; - } - - switch (backslash[3]) { - case '\\': - return unescape(dest, dest_length, backslash + 3, end, error_list, flags | YP_UNESCAPE_FLAG_CONTROL, write_to_str); - case '?': - if (write_to_str) { - dest[(*dest_length)++] = (char) unescape_char(0x7f, flags); - } - return backslash + 4; - default: - if (!char_is_ascii_printable(backslash[3])) { - yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Invalid control escape sequence"); - return backslash + 2; - } - - if (write_to_str) { - dest[(*dest_length)++] = (char) unescape_char((const unsigned char) backslash[3], flags | YP_UNESCAPE_FLAG_CONTROL); - } - return backslash + 4; - } - // \M-\C-x meta control character, where x is an ASCII printable character - // \M-\cx meta control character, where x is an ASCII printable character - // \M-x meta character, where x is an ASCII printable character - case 'M': { - if (backslash + 3 >= end) { - yp_diagnostic_list_append(error_list, backslash, backslash + 1, "Invalid control escape sequence"); - return end; - } - - if (flags & YP_UNESCAPE_FLAG_META) { - yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Meta escape sequence cannot be doubled."); - return backslash + 2; - } - - if (backslash[2] != '-') { - yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Invalid meta escape sequence"); - return backslash + 2; - } - - if (backslash[3] == '\\') { - return unescape(dest, dest_length, backslash + 3, end, error_list, flags | YP_UNESCAPE_FLAG_META, write_to_str); - } - - if (char_is_ascii_printable(backslash[3])) { - if (write_to_str) { - dest[(*dest_length)++] = (char) unescape_char((const unsigned char) backslash[3], flags | YP_UNESCAPE_FLAG_META); - } - return backslash + 4; - } - - yp_diagnostic_list_append(error_list, backslash, backslash + 2, "Invalid meta escape sequence"); - return backslash + 3; - } - // In this case we're escaping something that doesn't need escaping. - default: - { - if (write_to_str) { - dest[(*dest_length)++] = backslash[1]; - } - return backslash + 2; - } - } -} - -/******************************************************************************/ -/* Public functions and entrypoints */ -/******************************************************************************/ - -// Unescape the contents of the given token into the given string using the -// given unescape mode. The supported escapes are: -// -// \a bell, ASCII 07h (BEL) -// \b backspace, ASCII 08h (BS) -// \t horizontal tab, ASCII 09h (TAB) -// \n newline (line feed), ASCII 0Ah (LF) -// \v vertical tab, ASCII 0Bh (VT) -// \f form feed, ASCII 0Ch (FF) -// \r carriage return, ASCII 0Dh (CR) -// \e escape, ASCII 1Bh (ESC) -// \s space, ASCII 20h (SPC) -// \\ backslash -// \nnn octal bit pattern, where nnn is 1-3 octal digits ([0-7]) -// \xnn hexadecimal bit pattern, where nn is 1-2 hexadecimal digits ([0-9a-fA-F]) -// \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F]) -// \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F]) -// \cx or \C-x control character, where x is an ASCII printable character -// \M-x meta character, where x is an ASCII printable character -// \M-\C-x meta control character, where x is an ASCII printable character -// \M-\cx same as above -// \c\M-x same as above -// \c? or \C-? delete, ASCII 7Fh (DEL) -// -__attribute__((__visibility__("default"))) extern void -yp_unescape_manipulate_string(const char *value, size_t length, yp_string_t *string, yp_unescape_type_t unescape_type, yp_list_t *error_list) { - if (unescape_type == YP_UNESCAPE_NONE) { - // If we're not unescaping then we can reference the source directly. - yp_string_shared_init(string, value, value + length); - return; - } - - const char *backslash = memchr(value, '\\', length); - - if (backslash == NULL) { - // Here there are no escapes, so we can reference the source directly. - yp_string_shared_init(string, value, value + length); - return; - } - - // Here we have found an escape character, so we need to handle all escapes - // within the string. - yp_string_owned_init(string, malloc(length), length); - - // This is the memory address where we're putting the unescaped string. - char *dest = string->as.owned.source; - size_t dest_length = 0; - - // This is the current position in the source string that we're looking at. - // It's going to move along behind the backslash so that we can copy each - // segment of the string that doesn't contain an escape. - const char *cursor = value; - const char *end = value + length; - - // For each escape found in the source string, we will handle it and update - // the moving cursor->backslash window. - while (backslash != NULL && backslash < end) { - assert(dest_length < length); - - // This is the size of the segment of the string from the previous escape - // or the start of the string to the current escape. - size_t segment_size = (size_t) (backslash - cursor); - - // Here we're going to copy everything up until the escape into the - // destination buffer. - memcpy(dest + dest_length, cursor, segment_size); - dest_length += segment_size; - - switch (backslash[1]) { - case '\\': - case '\'': - dest[dest_length++] = (char) unescape_chars[(unsigned char) backslash[1]]; - cursor = backslash + 2; - break; - default: - if (unescape_type == YP_UNESCAPE_MINIMAL) { - // In this case we're escaping something that doesn't need escaping. - dest[dest_length++] = '\\'; - cursor = backslash + 1; - break; - } - - // This is the only type of unescaping left. In this case we need to - // handle all of the different unescapes. - assert(unescape_type == YP_UNESCAPE_ALL); - cursor = unescape(dest, &dest_length, backslash, end, error_list, YP_UNESCAPE_FLAG_NONE, true); - break; - } - - if (end > cursor) { - backslash = memchr(cursor, '\\', (size_t) (end - cursor)); - } else { - backslash = NULL; - } - } - - // We need to copy the final segment of the string after the last escape. - if (end > cursor) { - memcpy(dest + dest_length, cursor, (size_t) (end - cursor)); - } else { - cursor = end; - } - - // We also need to update the length at the end. This is because every escape - // reduces the length of the final string, and we don't want garbage at the - // end. - string->as.owned.length = dest_length + ((size_t) (end - cursor)); -} - -// This function is similar to yp_unescape_manipulate_string, except it doesn't -// actually perform any string manipulations. Instead, it calculates how long -// the unescaped character is, and returns that value -__attribute__((__visibility__("default"))) extern size_t -yp_unescape_calculate_difference(const char *backslash, const char *end, yp_unescape_type_t unescape_type, bool expect_single_codepoint, yp_list_t *error_list) { - assert(unescape_type != YP_UNESCAPE_NONE); - - switch (backslash[1]) { - case '\\': - case '\'': - return 2; - default: { - if (unescape_type == YP_UNESCAPE_MINIMAL) return 2; - - // This is the only type of unescaping left. In this case we need to - // handle all of the different unescapes. - assert(unescape_type == YP_UNESCAPE_ALL); - - unsigned char flags = YP_UNESCAPE_FLAG_NONE; - if (expect_single_codepoint) - flags |= YP_UNESCAPE_FLAG_EXPECT_SINGLE; - - const char *cursor = unescape(NULL, 0, backslash, end, error_list, flags, false); - assert(cursor > backslash); - - return (size_t) (cursor - backslash); - } - } -} diff --git a/src/main/c/yarp/src/yarp/src/util/yp_buffer.c b/src/main/c/yarp/src/yarp/src/util/yp_buffer.c deleted file mode 100644 index a9d764b93d38..000000000000 --- a/src/main/c/yarp/src/yarp/src/util/yp_buffer.c +++ /dev/null @@ -1,69 +0,0 @@ -#include "yarp/util/yp_buffer.h" - -#define YP_BUFFER_INITIAL_SIZE 1024 - -// Allocate a new yp_buffer_t. -yp_buffer_t * -yp_buffer_alloc(void) { - return (yp_buffer_t *) malloc(sizeof(yp_buffer_t)); -} - -// Initialize a yp_buffer_t with its default values. -void -yp_buffer_init(yp_buffer_t *buffer) { - buffer->value = (char *) malloc(YP_BUFFER_INITIAL_SIZE); - buffer->length = 0; - buffer->capacity = YP_BUFFER_INITIAL_SIZE; -} - -// Append a generic pointer to memory to the buffer. -static inline void -yp_buffer_append(yp_buffer_t *buffer, const void *source, size_t length) { - size_t next_length = buffer->length + length; - - if (next_length > buffer->capacity) { - do { - buffer->capacity *= 2; - } while (next_length > buffer->capacity); - - buffer->value = realloc(buffer->value, buffer->capacity); - } - - memcpy(buffer->value + buffer->length, source, length); - buffer->length += length; -} - -// Append a string to the buffer. -void -yp_buffer_append_str(yp_buffer_t *buffer, const char *value, size_t length) { - const void *source = value; - yp_buffer_append(buffer, source, length); -} - -// Append a single byte to the buffer. -void -yp_buffer_append_u8(yp_buffer_t *buffer, uint8_t value) { - const void *source = &value; - yp_buffer_append(buffer, source, sizeof(uint8_t)); -} - -// Append a 32-bit unsigned integer to the buffer. -void -yp_buffer_append_u32(yp_buffer_t *buffer, uint32_t value) { - if (value < 128) { - yp_buffer_append_u8(buffer, (uint8_t) value); - } else { - uint32_t n = value; - while (n >= 128) { - yp_buffer_append_u8(buffer, (uint8_t) (n | 128)); - n >>= 7; - } - yp_buffer_append_u8(buffer, (uint8_t) n); - } -} - -// Free the memory associated with the buffer. -void -yp_buffer_free(yp_buffer_t *buffer) { - free(buffer->value); -} diff --git a/src/main/c/yarp/src/yarp/src/util/yp_char.c b/src/main/c/yarp/src/yarp/src/util/yp_char.c deleted file mode 100644 index 47300e7c4086..000000000000 --- a/src/main/c/yarp/src/yarp/src/util/yp_char.c +++ /dev/null @@ -1,203 +0,0 @@ -#include "yarp/util/yp_char.h" - -#define YP_CHAR_BIT_WHITESPACE (1 << 0) -#define YP_CHAR_BIT_INLINE_WHITESPACE (1 << 1) -#define YP_CHAR_BIT_REGEXP_OPTION (1 << 2) - -#define YP_NUMBER_BIT_BINARY_DIGIT (1 << 0) -#define YP_NUMBER_BIT_BINARY_NUMBER (1 << 1) -#define YP_NUMBER_BIT_OCTAL_DIGIT (1 << 2) -#define YP_NUMBER_BIT_OCTAL_NUMBER (1 << 3) -#define YP_NUMBER_BIT_DECIMAL_DIGIT (1 << 4) -#define YP_NUMBER_BIT_DECIMAL_NUMBER (1 << 5) -#define YP_NUMBER_BIT_HEXADECIMAL_DIGIT (1 << 6) -#define YP_NUMBER_BIT_HEXADECIMAL_NUMBER (1 << 7) - -static const unsigned char yp_char_table[256] = { -//0 1 2 3 4 5 6 7 8 9 A B C D E F - 0, 0, 0, 0, 0, 0, 0, 0, 0, 3, 1, 3, 3, 3, 0, 0, // 0x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 1x - 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 2x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 3x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 4x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 5x - 0, 0, 0, 0, 0, 4, 0, 0, 0, 4, 0, 0, 0, 4, 4, 4, // 6x - 0, 0, 0, 4, 0, 4, 0, 0, 4, 0, 0, 0, 0, 0, 0, 0, // 7x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 8x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 9x - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ax - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Bx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Cx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Dx - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Ex - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // Fx -}; - -static const unsigned char yp_number_table[256] = { - // 0 1 2 3 4 5 6 7 8 9 A B C D E F - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 0x - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 1x - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 2x - 0xff, 0xff, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xfc, 0xf0, 0xf0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 3x - 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 4x - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaa, // 5x - 0x00, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0xc0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 6x - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 7x - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 8x - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // 9x - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ax - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Bx - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Cx - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Dx - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Ex - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Fx -}; - -static inline size_t -yp_strspn_char_kind(const char *string, long length, unsigned char kind) { - if (length <= 0) return 0; - - size_t size = 0; - size_t maximum = (size_t) length; - - while (size < maximum && (yp_char_table[(unsigned char) string[size]] & kind)) size++; - return size; -} - -// Returns the number of characters at the start of the string that are -// whitespace. Disallows searching past the given maximum number of characters. -size_t -yp_strspn_whitespace(const char *string, long length) { - return yp_strspn_char_kind(string, length, YP_CHAR_BIT_WHITESPACE); -} - -// Returns the number of characters at the start of the string that are inline -// whitespace. Disallows searching past the given maximum number of characters. -size_t -yp_strspn_inline_whitespace(const char *string, long length) { - return yp_strspn_char_kind(string, length, YP_CHAR_BIT_INLINE_WHITESPACE); -} - -// Returns the number of characters at the start of the string that are regexp -// options. Disallows searching past the given maximum number of characters. -size_t -yp_strspn_regexp_option(const char *string, long length) { - return yp_strspn_char_kind(string, length, YP_CHAR_BIT_REGEXP_OPTION); -} - -static inline bool -yp_char_is_char_kind(const char c, unsigned char kind) { - return (yp_char_table[(unsigned char) c] & kind) != 0; -} - -// Returns true if the given character is a whitespace character. -bool -yp_char_is_whitespace(const char c) { - return yp_char_is_char_kind(c, YP_CHAR_BIT_WHITESPACE); -} - -// Returns true if the given character is an inline whitespace character. -bool -yp_char_is_inline_whitespace(const char c) { - return yp_char_is_char_kind(c, YP_CHAR_BIT_INLINE_WHITESPACE); -} - -static inline size_t -yp_strspn_number_kind(const char *string, long length, unsigned char kind) { - if (length <= 0) return 0; - - size_t size = 0; - size_t maximum = (size_t) length; - - while (size < maximum && (yp_number_table[(unsigned char) string[size]] & kind)) size++; - return size; -} - -// Returns the number of characters at the start of the string that are binary -// digits or underscores. Disallows searching past the given maximum number of -// characters. -size_t -yp_strspn_binary_number(const char *string, long length) { - return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_BINARY_NUMBER); -} - -// Returns the number of characters at the start of the string that are octal -// digits or underscores. Disallows searching past the given maximum number of -// characters. -size_t -yp_strspn_octal_number(const char *string, long length) { - return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_OCTAL_NUMBER); -} - -// Returns the number of characters at the start of the string that are decimal -// digits. Disallows searching past the given maximum number of characters. -size_t -yp_strspn_decimal_digit(const char *string, long length) { - return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_DECIMAL_DIGIT); -} - -// Returns the number of characters at the start of the string that are decimal -// digits or underscores. Disallows searching past the given maximum number of -// characters. -size_t -yp_strspn_decimal_number(const char *string, long length) { - return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_DECIMAL_NUMBER); -} - -// Returns the number of characters at the start of the string that are -// hexadecimal digits. Disallows searching past the given maximum number of -// characters. -size_t -yp_strspn_hexadecimal_digit(const char *string, long length) { - return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_HEXADECIMAL_DIGIT); -} - -// Returns the number of characters at the start of the string that are -// hexadecimal digits or underscores. Disallows searching past the given maximum -// number of characters. -size_t -yp_strspn_hexadecimal_number(const char *string, long length) { - return yp_strspn_number_kind(string, length, YP_NUMBER_BIT_HEXADECIMAL_NUMBER); -} - -static inline bool -yp_char_is_number_kind(const char c, unsigned char kind) { - return (yp_number_table[(unsigned char) c] & kind) != 0; -} - -// Returns true if the given character is a binary digit. -bool -yp_char_is_binary_digit(const char c) { - return yp_char_is_number_kind(c, YP_NUMBER_BIT_BINARY_DIGIT); -} - -// Returns true if the given character is an octal digit. -bool -yp_char_is_octal_digit(const char c) { - return yp_char_is_number_kind(c, YP_NUMBER_BIT_OCTAL_DIGIT); -} - -// Returns true if the given character is a decimal digit. -bool -yp_char_is_decimal_digit(const char c) { - return yp_char_is_number_kind(c, YP_NUMBER_BIT_DECIMAL_DIGIT); -} - -// Returns true if the given character is a hexadecimal digit. -bool -yp_char_is_hexadecimal_digit(const char c) { - return yp_char_is_number_kind(c, YP_NUMBER_BIT_HEXADECIMAL_DIGIT); -} - -#undef YP_CHAR_BIT_WHITESPACE -#undef YP_CHAR_BIT_INLINE_WHITESPACE -#undef YP_CHAR_BIT_REGEXP_OPTION - -#undef YP_NUMBER_BIT_BINARY_DIGIT -#undef YP_NUMBER_BIT_BINARY_NUMBER -#undef YP_NUMBER_BIT_OCTAL_DIGIT -#undef YP_NUMBER_BIT_OCTAL_NUMBER -#undef YP_NUMBER_BIT_DECIMAL_DIGIT -#undef YP_NUMBER_BIT_DECIMAL_NUMBER -#undef YP_NUMBER_BIT_HEXADECIMAL_NUMBER -#undef YP_NUMBER_BIT_HEXADECIMAL_DIGIT diff --git a/src/main/c/yarp/src/yarp/src/util/yp_conversion.c b/src/main/c/yarp/src/yarp/src/util/yp_conversion.c deleted file mode 100644 index c6df9a2f2345..000000000000 --- a/src/main/c/yarp/src/yarp/src/util/yp_conversion.c +++ /dev/null @@ -1,13 +0,0 @@ -#include "yarp/util/yp_conversion.h" - -uint32_t -yp_long_to_u32(long value) { - assert(value >= 0 && value < UINT32_MAX); - return (uint32_t) value; -} - -uint32_t -yp_ulong_to_u32(unsigned long value) { - assert(value < UINT32_MAX); - return (uint32_t) value; -} diff --git a/src/main/c/yarp/src/yarp/src/util/yp_list.c b/src/main/c/yarp/src/yarp/src/util/yp_list.c deleted file mode 100644 index f57c65b780e1..000000000000 --- a/src/main/c/yarp/src/yarp/src/util/yp_list.c +++ /dev/null @@ -1,43 +0,0 @@ -#include "yarp/util/yp_list.h" - -// Allocate a new list. -yp_list_t * -yp_list_alloc(void) { - return malloc(sizeof(yp_list_t)); -} - -// Initializes a new list. -__attribute__((__visibility__("default"))) extern void -yp_list_init(yp_list_t *list) { - *list = (yp_list_t) { .head = NULL, .tail = NULL }; -} - -// Returns true if the given list is empty. -__attribute__((__visibility__("default"))) extern bool -yp_list_empty_p(yp_list_t *list) { - return list->head == NULL; -} - -// Append a node to the given list. -void -yp_list_append(yp_list_t *list, yp_list_node_t *node) { - if (list->head == NULL) { - list->head = node; - } else { - list->tail->next = node; - } - list->tail = node; -} - -// Deallocate the internal state of the given list. -__attribute__((__visibility__("default"))) extern void -yp_list_free(yp_list_t *list) { - yp_list_node_t *node = list->head; - yp_list_node_t *next; - - while (node != NULL) { - next = node->next; - free(node); - node = next; - } -} diff --git a/src/main/c/yarp/src/yarp/src/util/yp_string.c b/src/main/c/yarp/src/yarp/src/util/yp_string.c deleted file mode 100644 index d1ea7d1c0359..000000000000 --- a/src/main/c/yarp/src/yarp/src/util/yp_string.c +++ /dev/null @@ -1,94 +0,0 @@ -#include "yarp/util/yp_string.h" - -// Allocate a new yp_string_t. -yp_string_t * -yp_string_alloc(void) { - return (yp_string_t *) malloc(sizeof(yp_string_t)); -} - -// Initialize a shared string that is based on initial input. -void -yp_string_shared_init(yp_string_t *string, const char *start, const char *end) { - *string = (yp_string_t) { - .type = YP_STRING_SHARED, - .as.shared = { - .start = start, - .end = end - } - }; -} - -// Initialize an owned string that is responsible for freeing allocated memory. -void -yp_string_owned_init(yp_string_t *string, char *source, size_t length) { - *string = (yp_string_t) { - .type = YP_STRING_OWNED, - .as.owned = { - .source = source, - .length = length - } - }; -} - -// Initialize a constant string that doesn't own its memory source. -void -yp_string_constant_init(yp_string_t *string, const char *source, size_t length) { - *string = (yp_string_t) { - .type = YP_STRING_CONSTANT, - .as.constant = { - .source = source, - .length = length - } - }; -} - -// Returns the memory size associated with the string. -size_t -yp_string_memsize(const yp_string_t *string) { - size_t size = sizeof(yp_string_t); - if (string->type == YP_STRING_OWNED) { - size += string->as.owned.length; - } - return size; -} - -// Ensure the string is owned. If it is not, then reinitialize it as owned and -// copy over the previous source. -void -yp_string_ensure_owned(yp_string_t *string) { - if (string->type == YP_STRING_OWNED) return; - - size_t length = yp_string_length(string); - const char *source = yp_string_source(string); - - yp_string_owned_init(string, malloc(length), length); - memcpy(string->as.owned.source, source, length); -} - -// Returns the length associated with the string. -__attribute__ ((__visibility__("default"))) extern size_t -yp_string_length(const yp_string_t *string) { - if (string->type == YP_STRING_SHARED) { - return (size_t) (string->as.shared.end - string->as.shared.start); - } else { - return string->as.owned.length; - } -} - -// Returns the start pointer associated with the string. -__attribute__ ((__visibility__("default"))) extern const char * -yp_string_source(const yp_string_t *string) { - if (string->type == YP_STRING_SHARED) { - return string->as.shared.start; - } else { - return string->as.owned.source; - } -} - -// Free the associated memory of the given string. -__attribute__((__visibility__("default"))) extern void -yp_string_free(yp_string_t *string) { - if (string->type == YP_STRING_OWNED) { - free(string->as.owned.source); - } -} diff --git a/src/main/c/yarp/src/yarp/src/util/yp_string_list.c b/src/main/c/yarp/src/yarp/src/util/yp_string_list.c deleted file mode 100644 index e5157b2905f4..000000000000 --- a/src/main/c/yarp/src/yarp/src/util/yp_string_list.c +++ /dev/null @@ -1,32 +0,0 @@ -#include "yarp/util/yp_string_list.h" - -// Allocate a new yp_string_list_t. -yp_string_list_t * -yp_string_list_alloc(void) { - return (yp_string_list_t *) malloc(sizeof(yp_string_list_t)); -} - -// Initialize a yp_string_list_t with its default values. -void -yp_string_list_init(yp_string_list_t *string_list) { - string_list->strings = (yp_string_t *) malloc(sizeof(yp_string_t)); - string_list->length = 0; - string_list->capacity = 1; -} - -// Append a yp_string_t to the given string list. -void -yp_string_list_append(yp_string_list_t *string_list, yp_string_t *string) { - if (string_list->length + 1 > string_list->capacity) { - string_list->capacity *= 2; - string_list->strings = realloc(string_list->strings, string_list->capacity * sizeof(yp_string_t)); - } - - string_list->strings[string_list->length++] = *string; -} - -// Free the memory associated with the string list. -void -yp_string_list_free(yp_string_list_t *string_list) { - free(string_list->strings); -} diff --git a/src/main/c/yarp/src/yarp/src/util/yp_strpbrk.c b/src/main/c/yarp/src/yarp/src/util/yp_strpbrk.c deleted file mode 100644 index 8e04a227b79f..000000000000 --- a/src/main/c/yarp/src/yarp/src/util/yp_strpbrk.c +++ /dev/null @@ -1,30 +0,0 @@ -#include "yarp/util/yp_strpbrk.h" - -// Here we have rolled our own version of strpbrk. The standard library strpbrk -// has undefined behavior when the source string is not null-terminated. We want -// to support strings that are not null-terminated because yp_parse does not -// have the contract that the string is null-terminated. (This is desirable -// because it means the extension can call yp_parse with the result of a call to -// mmap). -// -// The standard library strpbrk also does not support passing a maximum length -// to search. We want to support this for the reason mentioned above, but we -// also don't want it to stop on null bytes. Ruby actually allows null bytes -// within strings, comments, regular expressions, etc. So we need to be able to -// skip past them. -const char * -yp_strpbrk(const char *source, const char *charset, long length) { - if (length < 0) return NULL; - - size_t index = 0; - size_t maximum = (size_t) length; - - while (index < maximum) { - if (strchr(charset, source[index]) != NULL) { - return &source[index]; - } - index++; - } - - return NULL; -} diff --git a/src/main/c/yarp/src/yarp/src/yarp.c b/src/main/c/yarp/src/yarp/src/yarp.c deleted file mode 100644 index 1eff0f51e3ec..000000000000 --- a/src/main/c/yarp/src/yarp/src/yarp.c +++ /dev/null @@ -1,12066 +0,0 @@ -#include "yarp.h" - -#define YP_STRINGIZE0(expr) #expr -#define YP_STRINGIZE(expr) YP_STRINGIZE0(expr) -#define YP_VERSION_MACRO YP_STRINGIZE(YP_VERSION_MAJOR) "." YP_STRINGIZE(YP_VERSION_MINOR) "." YP_STRINGIZE(YP_VERSION_PATCH) - -#define YP_TAB_WHITESPACE_SIZE 8 - -const char * -yp_version(void) { - return YP_VERSION_MACRO; -} - -#ifndef YP_DEBUG -#define YP_DEBUG 0 -#endif - -#if YP_DEBUG - -/******************************************************************************/ -/* Debugging */ -/******************************************************************************/ - -__attribute__((unused)) static const char * -debug_context(yp_context_t context) { - switch (context) { - case YP_CONTEXT_BEGIN: return "BEGIN"; - case YP_CONTEXT_CLASS: return "CLASS"; - case YP_CONTEXT_CASE_IN: return "CASE_IN"; - case YP_CONTEXT_CASE_WHEN: return "CASE_WHEN"; - case YP_CONTEXT_DEF: return "DEF"; - case YP_CONTEXT_DEF_PARAMS: return "DEF_PARAMS"; - case YP_CONTEXT_DEFAULT_PARAMS: return "DEFAULT_PARAMS"; - case YP_CONTEXT_ENSURE: return "ENSURE"; - case YP_CONTEXT_ELSE: return "ELSE"; - case YP_CONTEXT_ELSIF: return "ELSIF"; - case YP_CONTEXT_EMBEXPR: return "EMBEXPR"; - case YP_CONTEXT_BLOCK_BRACES: return "BLOCK_BRACES"; - case YP_CONTEXT_BLOCK_KEYWORDS: return "BLOCK_KEYWORDS"; - case YP_CONTEXT_FOR: return "FOR"; - case YP_CONTEXT_IF: return "IF"; - case YP_CONTEXT_MAIN: return "MAIN"; - case YP_CONTEXT_MODULE: return "MODULE"; - case YP_CONTEXT_PARENS: return "PARENS"; - case YP_CONTEXT_POSTEXE: return "POSTEXE"; - case YP_CONTEXT_PREDICATE: return "PREDICATE"; - case YP_CONTEXT_PREEXE: return "PREEXE"; - case YP_CONTEXT_RESCUE: return "RESCUE"; - case YP_CONTEXT_RESCUE_ELSE: return "RESCUE_ELSE"; - case YP_CONTEXT_SCLASS: return "SCLASS"; - case YP_CONTEXT_UNLESS: return "UNLESS"; - case YP_CONTEXT_UNTIL: return "UNTIL"; - case YP_CONTEXT_WHILE: return "WHILE"; - case YP_CONTEXT_LAMBDA_BRACES: return "LAMBDA_BRACES"; - case YP_CONTEXT_LAMBDA_DO_END: return "LAMBDA_DO_END"; - } - return NULL; -} - -__attribute__((unused)) static void -debug_contexts(yp_parser_t *parser) { - yp_context_node_t *context_node = parser->current_context; - fprintf(stderr, "CONTEXTS: "); - - if (context_node != NULL) { - while (context_node != NULL) { - fprintf(stderr, "%s", debug_context(context_node->context)); - context_node = context_node->prev; - if (context_node != NULL) { - fprintf(stderr, " <- "); - } - } - } else { - fprintf(stderr, "NONE"); - } - - fprintf(stderr, "\n"); -} - -__attribute__((unused)) static void -debug_node(const char *message, yp_parser_t *parser, yp_node_t *node) { - yp_buffer_t buffer; - yp_buffer_init(&buffer); - yp_prettyprint(parser, node, &buffer); - - fprintf(stderr, "%s\n%.*s\n", message, (int) buffer.length, buffer.value); - yp_buffer_free(&buffer); -} - -__attribute__((unused)) static void -debug_lex_mode(yp_parser_t *parser) { - yp_lex_mode_t *lex_mode = parser->lex_modes.current; - bool first = true; - - while (lex_mode != NULL) { - if (first) { - first = false; - } else { - fprintf(stderr, " <- "); - } - - switch (lex_mode->mode) { - case YP_LEX_DEFAULT: fprintf(stderr, "DEFAULT"); break; - case YP_LEX_EMBEXPR: fprintf(stderr, "EMBEXPR"); break; - case YP_LEX_EMBVAR: fprintf(stderr, "EMBVAR"); break; - case YP_LEX_HEREDOC: fprintf(stderr, "HEREDOC"); break; - case YP_LEX_LIST: fprintf(stderr, "LIST (terminator=%c, interpolation=%d)", lex_mode->as.list.terminator, lex_mode->as.list.interpolation); break; - case YP_LEX_REGEXP: fprintf(stderr, "REGEXP (terminator=%c)", lex_mode->as.regexp.terminator); break; - case YP_LEX_STRING: fprintf(stderr, "STRING (terminator=%c, interpolation=%d)", lex_mode->as.string.terminator, lex_mode->as.string.interpolation); break; - case YP_LEX_NUMERIC: fprintf(stderr, "NUMERIC (token_type=%s)", yp_token_type_to_str(lex_mode->as.numeric.type)); break; - } - - lex_mode = lex_mode->prev; - } - - fprintf(stderr, "\n"); -} - -__attribute__((unused)) static void -debug_state(yp_parser_t *parser) { - fprintf(stderr, "STATE: "); - bool first = true; - - if (parser->lex_state == YP_LEX_STATE_NONE) { - fprintf(stderr, "NONE\n"); - return; - } - -#define CHECK_STATE(state) \ - if (parser->lex_state & state) { \ - if (!first) fprintf(stderr, "|"); \ - fprintf(stderr, "%s", #state); \ - first = false; \ - } - - CHECK_STATE(YP_LEX_STATE_BEG) - CHECK_STATE(YP_LEX_STATE_END) - CHECK_STATE(YP_LEX_STATE_ENDARG) - CHECK_STATE(YP_LEX_STATE_ENDFN) - CHECK_STATE(YP_LEX_STATE_ARG) - CHECK_STATE(YP_LEX_STATE_CMDARG) - CHECK_STATE(YP_LEX_STATE_MID) - CHECK_STATE(YP_LEX_STATE_FNAME) - CHECK_STATE(YP_LEX_STATE_DOT) - CHECK_STATE(YP_LEX_STATE_CLASS) - CHECK_STATE(YP_LEX_STATE_LABEL) - CHECK_STATE(YP_LEX_STATE_LABELED) - CHECK_STATE(YP_LEX_STATE_FITEM) - -#undef CHECK_STATE - - fprintf(stderr, "\n"); -} - -__attribute__((unused)) static void -debug_token(yp_token_t * token) { - fprintf(stderr, "%s: \"%.*s\"\n", yp_token_type_to_str(token->type), (int) (token->end - token->start), token->start); -} - -__attribute__((unused)) static void -debug_scope(yp_parser_t *parser) { - fprintf(stderr, "SCOPE:\n"); - - yp_token_list_t token_list = parser->current_scope->node->locals; - for (size_t index = 0; index < token_list.size; index++) { - debug_token(&token_list.tokens[index]); - } - - fprintf(stderr, "\n"); -} - -#endif - -/******************************************************************************/ -/* Lex mode manipulations */ -/******************************************************************************/ - -// Push a new lex state onto the stack. If we're still within the pre-allocated -// space of the lex state stack, then we'll just use a new slot. Otherwise we'll -// allocate a new pointer and use that. -static void -lex_mode_push(yp_parser_t *parser, yp_lex_mode_t lex_mode) { - lex_mode.prev = parser->lex_modes.current; - parser->lex_modes.index++; - - if (parser->lex_modes.index > YP_LEX_STACK_SIZE - 1) { - parser->lex_modes.current = (yp_lex_mode_t *) malloc(sizeof(yp_lex_mode_t)); - *parser->lex_modes.current = lex_mode; - } else { - parser->lex_modes.stack[parser->lex_modes.index] = lex_mode; - parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index]; - } -} - -// Pop the current lex state off the stack. If we're within the pre-allocated -// space of the lex state stack, then we'll just decrement the index. Otherwise -// we'll free the current pointer and use the previous pointer. -static void -lex_mode_pop(yp_parser_t *parser) { - if (parser->lex_modes.index == 0) { - parser->lex_modes.current->mode = YP_LEX_DEFAULT; - } else if (parser->lex_modes.index < YP_LEX_STACK_SIZE) { - parser->lex_modes.index--; - parser->lex_modes.current = &parser->lex_modes.stack[parser->lex_modes.index]; - } else { - parser->lex_modes.index--; - yp_lex_mode_t *prev = parser->lex_modes.current->prev; - free(parser->lex_modes.current); - parser->lex_modes.current = prev; - } -} - -// This is the equivalent of IS_lex_state is CRuby. -static inline bool -lex_state_p(yp_parser_t *parser, yp_lex_state_t state) { - return parser->lex_state & state; -} - -typedef enum { - YP_IGNORED_NEWLINE_NONE = 0, - YP_IGNORED_NEWLINE_ALL, - YP_IGNORED_NEWLINE_PATTERN -} yp_ignored_newline_type_t; - -static inline yp_ignored_newline_type_t -lex_state_ignored_p(yp_parser_t *parser) { - bool ignored = lex_state_p(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_CLASS | YP_LEX_STATE_FNAME | YP_LEX_STATE_DOT) && !lex_state_p(parser, YP_LEX_STATE_LABELED); - - if (ignored) { - return YP_IGNORED_NEWLINE_ALL; - } else if (parser->lex_state == (YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED)) { - return YP_IGNORED_NEWLINE_PATTERN; - } else { - return YP_IGNORED_NEWLINE_NONE; - } -} - -static inline bool -lex_state_beg_p(yp_parser_t *parser) { - return lex_state_p(parser, YP_LEX_STATE_BEG_ANY) || (parser->lex_state == (YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED)); -} - -static inline bool -lex_state_arg_p(yp_parser_t *parser) { - return lex_state_p(parser, YP_LEX_STATE_ARG_ANY); -} - -static inline bool -lex_state_spcarg_p(yp_parser_t *parser, bool space_seen) { - return lex_state_arg_p(parser) && space_seen && !yp_char_is_whitespace(*parser->current.end); -} - -static inline bool -lex_state_end_p(yp_parser_t *parser) { - return lex_state_p(parser, YP_LEX_STATE_END_ANY); -} - -// This is the equivalent of IS_AFTER_OPERATOR in CRuby. -static inline bool -lex_state_operator_p(yp_parser_t *parser) { - return lex_state_p(parser, YP_LEX_STATE_FNAME | YP_LEX_STATE_DOT); -} - -// Set the state of the lexer. This is defined as a function to be able to put a breakpoint in it. -static inline void -lex_state_set(yp_parser_t *parser, yp_lex_state_t state) { - parser->lex_state = state; -} - -#if YP_DEBUG -static inline void -debug_lex_state_set(yp_parser_t *parser, yp_lex_state_t state, char const * caller_name, int line_number) { - fprintf(stderr, "Caller: %s:%d\nPrevious: ", caller_name, line_number); - debug_state(parser); - lex_state_set(parser, state); - fprintf(stderr, "Now: "); - debug_state(parser); - fprintf(stderr, "\n"); -} - -#define lex_state_set(parser, state) debug_lex_state_set(parser, state, __func__, __LINE__) -#endif - -/******************************************************************************/ -/* Node-related functions */ -/******************************************************************************/ - -// In a lot of places in the tree you can have tokens that are not provided but -// that do not cause an error. For example, in a method call without -// parentheses. In these cases we set the token to the "not provided" type. For -// example: -// -// yp_token_t token; -// not_provided(&token, parser->previous.end); -// -static inline yp_token_t -not_provided(yp_parser_t *parser) { - return (yp_token_t) { .type = YP_TOKEN_NOT_PROVIDED, .start = parser->start, .end = parser->start }; -} - -#define YP_LOCATION_NULL_VALUE(parser) ((yp_location_t) { .start = parser->start, .end = parser->start }) -#define YP_LOCATION_TOKEN_VALUE(token) ((yp_location_t) { .start = (token)->start, .end = (token)->end }) -#define YP_LOCATION_NODE_VALUE(node) ((yp_location_t) { .start = (node)->location.start, .end = (node)->location.end }) -#define YP_LOCATION_NODE_BASE_VALUE(node) ((yp_location_t) { .start = (node)->base.location.start, .end = (node)->base.location.end }) -#define YP_OPTIONAL_LOCATION_TOKEN_VALUE(token) ((token)->type == YP_TOKEN_NOT_PROVIDED ? (yp_location_t) { .start = NULL, .end = NULL } : YP_LOCATION_TOKEN_VALUE(token)) -#define YP_TOKEN_NOT_PROVIDED_VALUE(parser) ((yp_token_t) { .type = YP_TOKEN_NOT_PROVIDED, .start = (parser)->start, .end = (parser)->start }) - -// This is a special out parameter to the parse_arguments_list function that -// includes opening and closing parentheses in addition to the arguments since -// it's so common. It is handy to use when passing argument information to one -// of the call node creation functions. -typedef struct { - yp_token_t opening; - yp_arguments_node_t *arguments; - yp_token_t closing; - yp_block_node_t *block; -} yp_arguments_t; - -// Initialize a stack-allocated yp_arguments_t struct to its default values and -// return it. -static inline yp_arguments_t -yp_arguments(yp_parser_t *parser) { - return (yp_arguments_t) { - .opening = YP_TOKEN_NOT_PROVIDED_VALUE(parser), - .arguments = NULL, - .closing = YP_TOKEN_NOT_PROVIDED_VALUE(parser), - .block = NULL - }; -} - -static inline void * -yp_alloc(__attribute__((unused)) yp_parser_t *parser, size_t size) { - return malloc(size); -} - -/******************************************************************************/ -/* Node creation functions */ -/******************************************************************************/ - -// Allocate and initialize a new StatementsNode node. -static yp_statements_node_t * -yp_statements_node_create(yp_parser_t *parser); - -// Append a new node to the given StatementsNode node's body. -static void -yp_statements_node_body_append(yp_statements_node_t *node, yp_node_t *statement); - -// Allocate a new MissingNode node. -static yp_missing_node_t * -yp_missing_node_create(yp_parser_t *parser, const char *start, const char *end) { - yp_missing_node_t *node = yp_alloc(parser, sizeof(yp_missing_node_t)); - *node = (yp_missing_node_t) {{ .type = YP_NODE_MISSING_NODE, .location = { .start = start, .end = end } }}; - return node; -} - -// Allocate and initialize a new alias node. -static yp_alias_node_t * -yp_alias_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *new_name, yp_node_t *old_name) { - assert(keyword->type == YP_TOKEN_KEYWORD_ALIAS); - yp_alias_node_t *node = yp_alloc(parser, sizeof(yp_alias_node_t)); - - *node = (yp_alias_node_t) { - { - .type = YP_NODE_ALIAS_NODE, - .location = { - .start = keyword->start, - .end = old_name->location.end - }, - }, - .new_name = new_name, - .old_name = old_name, - .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword) - }; - - return node; -} - -// Allocate a new AlternationPatternNode node. -static yp_alternation_pattern_node_t * -yp_alternation_pattern_node_create(yp_parser_t *parser, yp_node_t *left, yp_node_t *right, const yp_token_t *operator) { - yp_alternation_pattern_node_t *node = yp_alloc(parser, sizeof(yp_alternation_pattern_node_t)); - - *node = (yp_alternation_pattern_node_t) { - { - .type = YP_NODE_ALTERNATION_PATTERN_NODE, - .location = { - .start = left->location.start, - .end = right->location.end - }, - }, - .left = left, - .right = right, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate and initialize a new and node. -static yp_and_node_t * -yp_and_node_create(yp_parser_t *parser, yp_node_t *left, const yp_token_t *operator, yp_node_t *right) { - yp_and_node_t *node = yp_alloc(parser, sizeof(yp_and_node_t)); - - *node = (yp_and_node_t) { - { - .type = YP_NODE_AND_NODE, - .location = { - .start = left->location.start, - .end = right->location.end - }, - }, - .left = left, - .operator = *operator, - .right = right - }; - - return node; -} - -// Allocate an initialize a new arguments node. -static yp_arguments_node_t * -yp_arguments_node_create(yp_parser_t *parser) { - yp_arguments_node_t *node = yp_alloc(parser, sizeof(yp_arguments_node_t)); - - *node = (yp_arguments_node_t) { - { - .type = YP_NODE_ARGUMENTS_NODE, - .location = YP_LOCATION_NULL_VALUE(parser) - }, - .arguments = YP_EMPTY_NODE_LIST - }; - - return node; -} - -// Return the size of the given arguments node. -static size_t -yp_arguments_node_size(yp_arguments_node_t *node) { - return node->arguments.size; -} - -// Append an argument to an arguments node. -static void -yp_arguments_node_arguments_append(yp_arguments_node_t *node, yp_node_t *argument) { - if (yp_arguments_node_size(node) == 0) { - node->base.location.start = argument->location.start; - } - - node->base.location.end = argument->location.end; - yp_node_list_append(&node->arguments, argument); -} - -// Allocate and initialize a new ArrayNode node. -static yp_array_node_t * -yp_array_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *closing) { - yp_array_node_t *node = yp_alloc(parser, sizeof(yp_array_node_t)); - - *node = (yp_array_node_t) { - { - .type = YP_NODE_ARRAY_NODE, - .location = { - .start = opening->start, - .end = closing->end - }, - }, - .opening = *opening, - .closing = *closing - }; - - yp_node_list_init(&node->elements); - return node; -} - -// Return the size of the given array node. -static inline size_t -yp_array_node_size(yp_array_node_t *node) { - return node->elements.size; -} - -// Append an argument to an array node. -static inline void -yp_array_node_elements_append(yp_array_node_t *node, yp_node_t *element) { - yp_node_list_append(&node->elements, element); - node->base.location.end = element->location.end; -} - -// Set the closing token and end location of an array node. -static void -yp_array_node_close_set(yp_array_node_t *node, const yp_token_t *closing) { - assert(closing->type == YP_TOKEN_BRACKET_RIGHT || closing->type == YP_TOKEN_STRING_END || closing->type == YP_TOKEN_MISSING); - node->base.location.end = closing->end; - node->closing = *closing; -} - -// Allocate and initialize a new array pattern node. The node list given in the -// nodes parameter is guaranteed to have at least two nodes. -static yp_array_pattern_node_t * -yp_array_pattern_node_node_list_create(yp_parser_t *parser, yp_node_list_t *nodes) { - yp_array_pattern_node_t *node = yp_alloc(parser, sizeof(yp_array_pattern_node_t)); - - *node = (yp_array_pattern_node_t) { - { - .type = YP_NODE_ARRAY_PATTERN_NODE, - .location = { - .start = nodes->nodes[0]->location.start, - .end = nodes->nodes[nodes->size - 1]->location.end - }, - }, - .constant = NULL, - .rest = NULL - }; - - // For now we're going to just copy over each pointer manually. This could be - // much more efficient, as we could instead resize the node list. - yp_node_list_init(&node->requireds); - yp_node_list_init(&node->posts); - - bool found_rest = false; - for (size_t index = 0; index < nodes->size; index++) { - yp_node_t *child = nodes->nodes[index]; - - if (child->type == YP_NODE_SPLAT_NODE) { - node->rest = child; - found_rest = true; - } else if (found_rest) { - yp_node_list_append(&node->posts, child); - } else { - yp_node_list_append(&node->requireds, child); - } - } - - return node; -} - -// Allocate and initialize a new array pattern node from a single rest node. -static yp_array_pattern_node_t * -yp_array_pattern_node_rest_create(yp_parser_t *parser, yp_node_t *rest) { - yp_array_pattern_node_t *node = yp_alloc(parser, sizeof(yp_array_pattern_node_t)); - - *node = (yp_array_pattern_node_t) { - { - .type = YP_NODE_ARRAY_PATTERN_NODE, - .location = rest->location, - }, - .constant = NULL, - .rest = rest - }; - - yp_node_list_init(&node->requireds); - yp_node_list_init(&node->posts); - - return node; -} - -// Allocate and initialize a new array pattern node from a constant and opening -// and closing tokens. -static yp_array_pattern_node_t * -yp_array_pattern_node_constant_create(yp_parser_t *parser, yp_node_t *constant, const yp_token_t *opening, const yp_token_t *closing) { - yp_array_pattern_node_t *node = yp_alloc(parser, sizeof(yp_array_pattern_node_t)); - - *node = (yp_array_pattern_node_t) { - { - .type = YP_NODE_ARRAY_PATTERN_NODE, - .location = { - .start = constant->location.start, - .end = closing->end - }, - }, - .constant = constant, - .rest = NULL, - .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), - .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) - }; - - yp_node_list_init(&node->requireds); - yp_node_list_init(&node->posts); - - return node; -} - -// Allocate and initialize a new array pattern node from an opening and closing -// token. -static yp_array_pattern_node_t * -yp_array_pattern_node_empty_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *closing) { - yp_array_pattern_node_t *node = yp_alloc(parser, sizeof(yp_array_pattern_node_t)); - - *node = (yp_array_pattern_node_t) { - { - .type = YP_NODE_ARRAY_PATTERN_NODE, - .location = { - .start = opening->start, - .end = closing->end - }, - }, - .constant = NULL, - .rest = NULL, - .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), - .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) - }; - - yp_node_list_init(&node->requireds); - yp_node_list_init(&node->posts); - - return node; -} - -static inline void -yp_array_pattern_node_requireds_append(yp_array_pattern_node_t *node, yp_node_t *inner) { - yp_node_list_append(&node->requireds, inner); -} - -// Allocate and initialize a new assoc node. -static yp_assoc_node_t * -yp_assoc_node_create(yp_parser_t *parser, yp_node_t *key, const yp_token_t *operator, yp_node_t *value) { - yp_assoc_node_t *node = yp_alloc(parser, sizeof(yp_assoc_node_t)); - const char *end; - - if (value != NULL) { - end = value->location.end; - } else if (operator->type != YP_TOKEN_NOT_PROVIDED) { - end = operator->end; - } else { - end = key->location.end; - } - - *node = (yp_assoc_node_t) { - { - .type = YP_NODE_ASSOC_NODE, - .location = { - .start = key->location.start, - .end = end - }, - }, - .key = key, - .operator = *operator, - .value = value - }; - - return node; -} - -// Allocate and initialize a new assoc splat node. -static yp_assoc_splat_node_t * -yp_assoc_splat_node_create(yp_parser_t *parser, yp_node_t *value, const yp_token_t *operator) { - assert(operator->type == YP_TOKEN_USTAR_STAR); - yp_assoc_splat_node_t *node = yp_alloc(parser, sizeof(yp_assoc_splat_node_t)); - - *node = (yp_assoc_splat_node_t) { - { - .type = YP_NODE_ASSOC_SPLAT_NODE, - .location = { - .start = operator->start, - .end = value == NULL ? operator->end : value->location.end - }, - }, - .value = value, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate and initialize new a begin node. -static yp_begin_node_t * -yp_begin_node_create(yp_parser_t *parser, const yp_token_t *begin_keyword, yp_statements_node_t *statements) { - yp_begin_node_t *node = yp_alloc(parser, sizeof(yp_begin_node_t)); - - *node = (yp_begin_node_t) { - { - .type = YP_NODE_BEGIN_NODE, - .location = { - .start = begin_keyword->start, - .end = statements == NULL ? begin_keyword->end : statements->base.location.end - }, - }, - .begin_keyword = *begin_keyword, - .statements = statements, - .end_keyword = YP_TOKEN_NOT_PROVIDED_VALUE(parser) - }; - - return node; -} - -// Set the rescue clause and end location of a begin node. -static void -yp_begin_node_rescue_clause_set(yp_begin_node_t *node, yp_rescue_node_t *rescue_clause) { - node->base.location.end = rescue_clause->base.location.end; - node->rescue_clause = rescue_clause; -} - -// Set the else clause and end location of a begin node. -static void -yp_begin_node_else_clause_set(yp_begin_node_t *node, yp_else_node_t *else_clause) { - node->base.location.end = else_clause->base.location.end; - node->else_clause = else_clause; -} - -// Set the ensure clause and end location of a begin node. -static void -yp_begin_node_ensure_clause_set(yp_begin_node_t *node, yp_ensure_node_t *ensure_clause) { - node->base.location.end = ensure_clause->base.location.end; - node->ensure_clause = ensure_clause; -} - -// Set the end keyword and end location of a begin node. -static void -yp_begin_node_end_keyword_set(yp_begin_node_t *node, const yp_token_t *end_keyword) { - assert(end_keyword->type == YP_TOKEN_KEYWORD_END || end_keyword->type == YP_TOKEN_MISSING); - - node->base.location.end = end_keyword->end; - node->end_keyword = *end_keyword; -} - -// Allocate and initialize a new BlockArgumentNode node. -static yp_block_argument_node_t * -yp_block_argument_node_create(yp_parser_t *parser, const yp_token_t *operator, yp_node_t *expression) { - yp_block_argument_node_t *node = yp_alloc(parser, sizeof(yp_block_argument_node_t)); - - *node = (yp_block_argument_node_t) { - { - .type = YP_NODE_BLOCK_ARGUMENT_NODE, - .location = { - .start = operator->start, - .end = expression == NULL ? operator->end : expression->location.end - }, - }, - .expression = expression, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate and initialize a new BlockNode node. -static yp_block_node_t * -yp_block_node_create(yp_parser_t *parser, yp_token_list_t *locals, const yp_token_t *opening, yp_block_parameters_node_t *parameters, yp_node_t *statements, const yp_token_t *closing) { - yp_block_node_t *node = yp_alloc(parser, sizeof(yp_block_node_t)); - - *node = (yp_block_node_t) { - { - .type = YP_NODE_BLOCK_NODE, - .location = { .start = opening->start, .end = closing->end }, - }, - .locals = *locals, - .parameters = parameters, - .statements = statements, - .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), - .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) - }; - - return node; -} - -// Allocate and initialize a new BlockParameterNode node. -static yp_block_parameter_node_t * -yp_block_parameter_node_create(yp_parser_t *parser, const yp_token_t *name, const yp_token_t *operator) { - assert(operator->type == YP_TOKEN_NOT_PROVIDED || operator->type == YP_TOKEN_AMPERSAND); - yp_block_parameter_node_t *node = yp_alloc(parser, sizeof(yp_block_parameter_node_t)); - - *node = (yp_block_parameter_node_t) { - { - .type = YP_NODE_BLOCK_PARAMETER_NODE, - .location = { - .start = operator->start, - .end = (name->type == YP_TOKEN_NOT_PROVIDED ? operator->end : name->end) - }, - }, - .name = *name, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate and initialize a new BlockParametersNode node. -static yp_block_parameters_node_t * -yp_block_parameters_node_create(yp_parser_t *parser, yp_parameters_node_t *parameters, const yp_token_t *opening) { - yp_block_parameters_node_t *node = yp_alloc(parser, sizeof(yp_block_parameters_node_t)); - - const char *start; - if (opening->type != YP_TOKEN_NOT_PROVIDED) { - start = opening->start; - } else if (parameters != NULL) { - start = parameters->base.location.start; - } else { - start = NULL; - } - - const char *end; - if (parameters != NULL) { - end = parameters->base.location.end; - } else if (opening->type != YP_TOKEN_NOT_PROVIDED) { - end = opening->end; - } else { - end = NULL; - } - - *node = (yp_block_parameters_node_t) { - { - .type = YP_NODE_BLOCK_PARAMETERS_NODE, - .location = { - .start = start, - .end = end - } - }, - .parameters = parameters, - .opening_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(opening), - .closing_loc = { .start = NULL, .end = NULL } - }; - - yp_token_list_init(&node->locals); - return node; -} - -// Set the closing location of a BlockParametersNode node. -static void -yp_block_parameters_node_closing_set(yp_block_parameters_node_t *node, const yp_token_t *closing) { - assert(closing->type == YP_TOKEN_PIPE || closing->type == YP_TOKEN_PARENTHESIS_RIGHT || closing->type == YP_TOKEN_MISSING); - - node->base.location.end = closing->end; - node->closing_loc = YP_LOCATION_TOKEN_VALUE(closing); -} - -// Append a new block-local variable to a BlockParametersNode node. -static void -yp_block_parameters_node_append_local(yp_block_parameters_node_t *node, const yp_token_t *local) { - assert(local->type == YP_TOKEN_IDENTIFIER); - - yp_token_list_append(&node->locals, local); - if (node->base.location.start == NULL) node->base.location.start = local->start; - node->base.location.end = local->end; -} - -// Allocate and initialize a new BreakNode node. -static yp_break_node_t * -yp_break_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_arguments_node_t *arguments) { - assert(keyword->type == YP_TOKEN_KEYWORD_BREAK); - yp_break_node_t *node = yp_alloc(parser, sizeof(yp_break_node_t)); - - *node = (yp_break_node_t) { - { - .type = YP_NODE_BREAK_NODE, - .location = { - .start = keyword->start, - .end = (arguments == NULL ? keyword->end : arguments->base.location.end) - }, - }, - .arguments = arguments, - .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword) - }; - - return node; -} - -// Allocate and initialize a new CallNode node. This sets everything to NULL or -// YP_TOKEN_NOT_PROVIDED as appropriate such that its values can be overridden -// in the various specializations of this function. -static yp_call_node_t * -yp_call_node_create(yp_parser_t *parser) { - yp_call_node_t *node = yp_alloc(parser, sizeof(yp_call_node_t)); - - *node = (yp_call_node_t) { - { - .type = YP_NODE_CALL_NODE, - .location = YP_LOCATION_NULL_VALUE(parser), - }, - .receiver = NULL, - .call_operator = YP_TOKEN_NOT_PROVIDED_VALUE(parser), - .message = YP_TOKEN_NOT_PROVIDED_VALUE(parser), - .opening = YP_TOKEN_NOT_PROVIDED_VALUE(parser), - .arguments = NULL, - .closing = YP_TOKEN_NOT_PROVIDED_VALUE(parser), - .block = NULL - }; - - return node; -} - -// Allocate and initialize a new CallNode node from an aref or an aset -// expression. -static yp_call_node_t * -yp_call_node_aref_create(yp_parser_t *parser, yp_node_t *receiver, yp_arguments_t *arguments) { - yp_call_node_t *node = yp_call_node_create(parser); - - node->base.location.start = receiver->location.start; - if (arguments->block != NULL) { - node->base.location.end = arguments->block->base.location.end; - } else { - node->base.location.end = arguments->closing.end; - } - - node->receiver = receiver; - node->message = (yp_token_t) { - .type = YP_TOKEN_BRACKET_LEFT_RIGHT, - .start = arguments->opening.start, - .end = arguments->opening.end - }; - - node->opening = arguments->opening; - node->arguments = arguments->arguments; - node->closing = arguments->closing; - node->block = arguments->block; - - yp_string_constant_init(&node->name, "[]", 2); - return node; -} - -// Allocate and initialize a new CallNode node from a binary expression. -static yp_call_node_t * -yp_call_node_binary_create(yp_parser_t *parser, yp_node_t *receiver, yp_token_t *operator, yp_node_t *argument) { - yp_call_node_t *node = yp_call_node_create(parser); - - node->base.location.start = receiver->location.start; - node->base.location.end = argument->location.end; - - node->receiver = receiver; - node->message = *operator; - - yp_arguments_node_t *arguments = yp_arguments_node_create(parser); - yp_arguments_node_arguments_append(arguments, argument); - node->arguments = arguments; - - yp_string_shared_init(&node->name, operator->start, operator->end); - return node; -} - -// Allocate and initialize a new CallNode node from a call expression. -static yp_call_node_t * -yp_call_node_call_create(yp_parser_t *parser, yp_node_t *receiver, yp_token_t *operator, yp_token_t *message, yp_arguments_t *arguments) { - yp_call_node_t *node = yp_call_node_create(parser); - - node->base.location.start = receiver->location.start; - if (arguments->block != NULL) { - node->base.location.end = arguments->block->base.location.end; - } else if (arguments->closing.type != YP_TOKEN_NOT_PROVIDED) { - node->base.location.end = arguments->closing.end; - } else if (arguments->arguments != NULL) { - node->base.location.end = arguments->arguments->base.location.end; - } else { - node->base.location.end = message->end; - } - - node->receiver = receiver; - node->call_operator = *operator; - node->message = *message; - node->opening = arguments->opening; - node->arguments = arguments->arguments; - node->closing = arguments->closing; - node->block = arguments->block; - - yp_string_shared_init(&node->name, message->start, message->end); - return node; -} - -// Allocate and initialize a new CallNode node from a call to a method name -// without a receiver that could not have been a local variable read. -static yp_call_node_t * -yp_call_node_fcall_create(yp_parser_t *parser, yp_token_t *message, yp_arguments_t *arguments) { - yp_call_node_t *node = yp_call_node_create(parser); - - node->base.location.start = message->start; - if (arguments->block != NULL) { - node->base.location.end = arguments->block->base.location.end; - } else if (arguments->closing.type != YP_TOKEN_NOT_PROVIDED) { - node->base.location.end = arguments->closing.end; - } else if (arguments->arguments != NULL) { - node->base.location.end = arguments->arguments->base.location.end; - } else { - node->base.location.end = arguments->closing.end; - } - - node->message = *message; - node->opening = arguments->opening; - node->arguments = arguments->arguments; - node->closing = arguments->closing; - node->block = arguments->block; - - yp_string_shared_init(&node->name, message->start, message->end); - return node; -} - -// Allocate and initialize a new CallNode node from a not expression. -static yp_call_node_t * -yp_call_node_not_create(yp_parser_t *parser, yp_node_t *receiver, yp_token_t *message, yp_arguments_t *arguments) { - yp_call_node_t *node = yp_call_node_create(parser); - - node->base.location.start = message->start; - if (arguments->closing.type != YP_TOKEN_NOT_PROVIDED) { - node->base.location.end = arguments->closing.end; - } else { - node->base.location.end = receiver->location.end; - } - - node->receiver = receiver; - node->message = *message; - node->opening = arguments->opening; - node->arguments = arguments->arguments; - node->closing = arguments->closing; - - yp_string_constant_init(&node->name, "!", 1); - return node; -} - -// Allocate and initialize a new CallNode node from a call shorthand expression. -static yp_call_node_t * -yp_call_node_shorthand_create(yp_parser_t *parser, yp_node_t *receiver, yp_token_t *operator, yp_arguments_t *arguments) { - yp_call_node_t *node = yp_call_node_create(parser); - - node->base.location.start = receiver->location.start; - if (arguments->block != NULL) { - node->base.location.end = arguments->block->base.location.end; - } else { - node->base.location.end = arguments->closing.end; - } - - node->receiver = receiver; - node->call_operator = *operator; - node->opening = arguments->opening; - node->arguments = arguments->arguments; - node->closing = arguments->closing; - node->block = arguments->block; - - yp_string_constant_init(&node->name, "call", 4); - return node; -} - -// Allocate and initialize a new CallNode node from a unary operator expression. -static yp_call_node_t * -yp_call_node_unary_create(yp_parser_t *parser, yp_token_t *operator, yp_node_t *receiver, const char *name) { - yp_call_node_t *node = yp_call_node_create(parser); - - node->base.location.start = operator->start; - node->base.location.end = receiver->location.end; - - node->receiver = receiver; - node->message = *operator; - - yp_string_constant_init(&node->name, name, strlen(name)); - return node; -} - -// Allocate and initialize a new CallNode node from a call to a method name -// without a receiver that could also have been a local variable read. -static yp_call_node_t * -yp_call_node_vcall_create(yp_parser_t *parser, yp_token_t *message) { - yp_call_node_t *node = yp_call_node_create(parser); - - node->base.location.start = message->start; - node->base.location.end = message->end; - - node->message = *message; - - yp_string_shared_init(&node->name, message->start, message->end); - return node; -} - -// Returns whether or not this call node is a "vcall" (a call to a method name -// without a receiver that could also have been a local variable read). -static inline bool -yp_call_node_vcall_p(yp_call_node_t *node) { - return ( - (node->opening.type == YP_TOKEN_NOT_PROVIDED) && - (node->arguments == NULL) && - (node->block == NULL) && - (node->receiver == NULL) - ); -} - -// Allocate and initialize a new CapturePatternNode node. -static yp_capture_pattern_node_t * -yp_capture_pattern_node_create(yp_parser_t *parser, yp_node_t *value, yp_node_t *target, const yp_token_t *operator) { - yp_capture_pattern_node_t *node = yp_alloc(parser, sizeof(yp_capture_pattern_node_t)); - - *node = (yp_capture_pattern_node_t) { - { - .type = YP_NODE_CAPTURE_PATTERN_NODE, - .location = { - .start = value->location.start, - .end = target->location.end - }, - }, - .value = value, - .target = target, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate and initialize a new CaseNode node. -static yp_case_node_t * -yp_case_node_create(yp_parser_t *parser, const yp_token_t *case_keyword, yp_node_t *predicate, yp_else_node_t *consequent, const yp_token_t *end_keyword) { - yp_case_node_t *node = yp_alloc(parser, sizeof(yp_case_node_t)); - - *node = (yp_case_node_t) { - { - .type = YP_NODE_CASE_NODE, - .location = { - .start = case_keyword->start, - .end = end_keyword->end - }, - }, - .predicate = predicate, - .consequent = consequent, - .case_keyword_loc = YP_LOCATION_TOKEN_VALUE(case_keyword), - .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword) - }; - - yp_node_list_init(&node->conditions); - return node; -} - -// Append a new condition to a CaseNode node. -static void -yp_case_node_condition_append(yp_case_node_t *node, yp_node_t *condition) { - assert(condition->type == YP_NODE_WHEN_NODE || condition->type == YP_NODE_IN_NODE); - - yp_node_list_append(&node->conditions, condition); - node->base.location.end = condition->location.end; -} - -// Set the consequent of a CaseNode node. -static void -yp_case_node_consequent_set(yp_case_node_t *node, yp_else_node_t *consequent) { - node->consequent = consequent; - node->base.location.end = consequent->base.location.end; -} - -// Set the end location for a CaseNode node. -static void -yp_case_node_end_keyword_loc_set(yp_case_node_t *node, const yp_token_t *end_keyword) { - node->base.location.end = end_keyword->end; - node->end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword); -} - -// Allocate a new ClassNode node. -static yp_class_node_t * -yp_class_node_create(yp_parser_t *parser, yp_token_list_t *locals, const yp_token_t *class_keyword, yp_node_t *constant_path, const yp_token_t *inheritance_operator, yp_node_t *superclass, yp_node_t *statements, const yp_token_t *end_keyword) { - yp_class_node_t *node = yp_alloc(parser, sizeof(yp_class_node_t)); - - *node = (yp_class_node_t) { - { - .type = YP_NODE_CLASS_NODE, - .location = { .start = class_keyword->start, .end = end_keyword->end }, - }, - .locals = *locals, - .class_keyword = *class_keyword, - .constant_path = constant_path, - .inheritance_operator = *inheritance_operator, - .superclass = superclass, - .statements = statements, - .end_keyword = *end_keyword - }; - - return node; -} - -// Allocate and initialize a new ClassVariableReadNode node. -static yp_class_variable_read_node_t * -yp_class_variable_read_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_CLASS_VARIABLE); - yp_class_variable_read_node_t *node = yp_alloc(parser, sizeof(yp_class_variable_read_node_t)); - *node = (yp_class_variable_read_node_t) {{ .type = YP_NODE_CLASS_VARIABLE_READ_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Initialize a new ClassVariableWriteNode node from a ClassVariableRead node. -static yp_class_variable_write_node_t * -yp_class_variable_read_node_to_class_variable_write_node(yp_parser_t *parser, yp_class_variable_read_node_t *read_node, yp_token_t *operator, yp_node_t *value) { - yp_class_variable_write_node_t *node = yp_alloc(parser, sizeof(yp_class_variable_write_node_t)); - - *node = (yp_class_variable_write_node_t) { - { - .type = YP_NODE_CLASS_VARIABLE_WRITE_NODE, - .location = { - .start = read_node->base.location.start, - .end = value != NULL ? value->location.end : read_node->base.location.end - }, - }, - .name_loc = YP_LOCATION_NODE_VALUE((yp_node_t *)read_node), - .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), - .value = value - }; - - return node; -} - -// Allocate and initialize a new ConstantPathNode node. -static yp_constant_path_node_t * -yp_constant_path_node_create(yp_parser_t *parser, yp_node_t *parent, const yp_token_t *delimiter, yp_node_t *child) { - yp_constant_path_node_t *node = yp_alloc(parser, sizeof(yp_constant_path_node_t)); - - *node = (yp_constant_path_node_t) { - { - .type = YP_NODE_CONSTANT_PATH_NODE, - .location = { - .start = parent == NULL ? delimiter->start : parent->location.start, - .end = child->location.end - }, - }, - .parent = parent, - .child = child, - .delimiter_loc = YP_LOCATION_TOKEN_VALUE(delimiter) - }; - - return node; -} - -// Allocate a new ConstantPathWriteNode node. -static yp_constant_path_write_node_t * -yp_constant_path_write_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { - yp_constant_path_write_node_t *node = yp_alloc(parser, sizeof(yp_constant_path_write_node_t)); - - *node = (yp_constant_path_write_node_t) { - { - .type = YP_NODE_CONSTANT_PATH_WRITE_NODE, - .location = { - .start = target->location.start, - .end = (value == NULL ? target->location.end : value->location.end) - }, - }, - .target = target, - .operator = *operator, - .value = value - }; - - return node; -} - -// Allocate and initialize a new ConstantReadNode node. -static yp_constant_read_node_t * -yp_constant_read_node_create(yp_parser_t *parser, const yp_token_t *name) { - assert(name->type == YP_TOKEN_CONSTANT || name->type == YP_TOKEN_MISSING); - - yp_constant_read_node_t *node = yp_alloc(parser, sizeof(yp_constant_read_node_t)); - *node = (yp_constant_read_node_t) {{ .type = YP_NODE_CONSTANT_READ_NODE, .location = YP_LOCATION_TOKEN_VALUE(name) }}; - return node; -} - -// Allocate and initialize a new DefNode node. -static yp_def_node_t * -yp_def_node_create( - yp_parser_t *parser, - const yp_token_t *name, - yp_node_t *receiver, - yp_parameters_node_t *parameters, - yp_node_t *statements, - yp_token_list_t *locals, - const yp_token_t *def_keyword, - const yp_token_t *operator, - const yp_token_t *lparen, - const yp_token_t *rparen, - const yp_token_t *equal, - const yp_token_t *end_keyword -) { - yp_def_node_t *node = yp_alloc(parser, sizeof(yp_def_node_t)); - const char *end; - - if (end_keyword->type == YP_TOKEN_NOT_PROVIDED) { - end = statements->location.end; - } else { - end = end_keyword->end; - } - - *node = (yp_def_node_t) { - { - .type = YP_NODE_DEF_NODE, - .location = { .start = def_keyword->start, .end = end }, - }, - .name = *name, - .receiver = receiver, - .parameters = parameters, - .statements = statements, - .locals = *locals, - .def_keyword_loc = YP_LOCATION_TOKEN_VALUE(def_keyword), - .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), - .lparen_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(lparen), - .rparen_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(rparen), - .equal_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(equal), - .end_keyword_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(end_keyword) - }; - - return node; -} - -// Allocate a new DefinedNode node. -static yp_defined_node_t * -yp_defined_node_create(yp_parser_t *parser, const yp_token_t *lparen, yp_node_t *value, const yp_token_t *rparen, const yp_location_t *keyword_loc) { - yp_defined_node_t *node = yp_alloc(parser, sizeof(yp_defined_node_t)); - - *node = (yp_defined_node_t) { - { - .type = YP_NODE_DEFINED_NODE, - .location = { - .start = keyword_loc->start, - .end = (rparen->type == YP_TOKEN_NOT_PROVIDED ? value->location.end : rparen->end) - }, - }, - .lparen = *lparen, - .value = value, - .rparen = *rparen, - .keyword_loc = *keyword_loc - }; - - return node; -} - -// Allocate and initialize a new ElseNode node. -static yp_else_node_t * -yp_else_node_create(yp_parser_t *parser, const yp_token_t *else_keyword, yp_statements_node_t *statements, const yp_token_t *end_keyword) { - yp_else_node_t *node = yp_alloc(parser, sizeof(yp_else_node_t)); - const char *end = NULL; - if ((end_keyword->type == YP_TOKEN_NOT_PROVIDED) && (statements != NULL)) { - end = statements->base.location.end; - } else { - end = end_keyword->end; - } - - *node = (yp_else_node_t) { - { - .type = YP_NODE_ELSE_NODE, - .location = { - .start = else_keyword->start, - .end = end, - }, - }, - .else_keyword = *else_keyword, - .statements = statements, - .end_keyword = *end_keyword - }; - - return node; -} - -// Allocate a new EnsureNode node. -static yp_ensure_node_t * -yp_ensure_node_create(yp_parser_t *parser, const yp_token_t *ensure_keyword, yp_statements_node_t *statements, const yp_token_t *end_keyword) { - yp_ensure_node_t *node = yp_alloc(parser, sizeof(yp_ensure_node_t)); - - *node = (yp_ensure_node_t) { - { - .type = YP_NODE_ENSURE_NODE, - .location = { - .start = ensure_keyword->start, - .end = end_keyword->end - }, - }, - .ensure_keyword = *ensure_keyword, - .statements = statements, - .end_keyword = *end_keyword - }; - - return node; -} - -// Allocate and initialize a new FalseNode node. -static yp_false_node_t * -yp_false_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_KEYWORD_FALSE); - yp_false_node_t *node = yp_alloc(parser, sizeof(yp_false_node_t)); - *node = (yp_false_node_t) {{ .type = YP_NODE_FALSE_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate and initialize a new find pattern node. The node list given in the -// nodes parameter is guaranteed to have at least two nodes. -static yp_find_pattern_node_t * -yp_find_pattern_node_create(yp_parser_t *parser, yp_node_list_t *nodes) { - yp_find_pattern_node_t *node = yp_alloc(parser, sizeof(yp_find_pattern_node_t)); - - *node = (yp_find_pattern_node_t) { - { - .type = YP_NODE_FIND_PATTERN_NODE, - .location = { - .start = nodes->nodes[0]->location.start, - .end = nodes->nodes[nodes->size - 1]->location.end - }, - }, - .constant = NULL, - .left = nodes->nodes[0], - .right = nodes->nodes[nodes->size - 1] - }; - - // For now we're going to just copy over each pointer manually. This could be - // much more efficient, as we could instead resize the node list to only point - // to 1...-1. - yp_node_list_init(&node->requireds); - for (size_t index = 1; index < nodes->size - 1; index++) { - yp_node_list_append(&node->requireds, nodes->nodes[index]); - } - - return node; -} - -// Allocate and initialize a new FloatNode node. -static yp_float_node_t * -yp_float_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_FLOAT); - yp_float_node_t *node = yp_alloc(parser, sizeof(yp_float_node_t)); - *node = (yp_float_node_t) {{ .type = YP_NODE_FLOAT_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate and initialize a new ForNode node. -static yp_for_node_t * -yp_for_node_create( - yp_parser_t *parser, - yp_node_t *index, - yp_node_t *collection, - yp_statements_node_t *statements, - const yp_token_t *for_keyword, - const yp_token_t *in_keyword, - const yp_token_t *do_keyword, - const yp_token_t *end_keyword -) { - yp_for_node_t *node = yp_alloc(parser, sizeof(yp_for_node_t)); - - *node = (yp_for_node_t) { - { - .type = YP_NODE_FOR_NODE, - .location = { - .start = for_keyword->start, - .end = end_keyword->end - }, - }, - .index = index, - .collection = collection, - .statements = statements, - .for_keyword_loc = YP_LOCATION_TOKEN_VALUE(for_keyword), - .in_keyword_loc = YP_LOCATION_TOKEN_VALUE(in_keyword), - .do_keyword_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(do_keyword), - .end_keyword_loc = YP_LOCATION_TOKEN_VALUE(end_keyword) - }; - - return node; -} - -// Allocate and initialize a new ForwardingArgumentsNode node. -static yp_forwarding_arguments_node_t * -yp_forwarding_arguments_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_UDOT_DOT_DOT); - yp_forwarding_arguments_node_t *node = yp_alloc(parser, sizeof(yp_forwarding_arguments_node_t)); - *node = (yp_forwarding_arguments_node_t) {{ .type = YP_NODE_FORWARDING_ARGUMENTS_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate and initialize a new ForwardingParameterNode node. -static yp_forwarding_parameter_node_t * -yp_forwarding_parameter_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_UDOT_DOT_DOT); - yp_forwarding_parameter_node_t *node = yp_alloc(parser, sizeof(yp_forwarding_parameter_node_t)); - *node = (yp_forwarding_parameter_node_t) {{ .type = YP_NODE_FORWARDING_PARAMETER_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate and initialize a new ForwardingSuper node. -static yp_forwarding_super_node_t * -yp_forwarding_super_node_create(yp_parser_t *parser, const yp_token_t *token, yp_arguments_t *arguments) { - assert(token->type == YP_TOKEN_KEYWORD_SUPER); - yp_forwarding_super_node_t *node = yp_alloc(parser, sizeof(yp_forwarding_super_node_t)); - - *node = (yp_forwarding_super_node_t) { - { - .type = YP_NODE_FORWARDING_SUPER_NODE, - .location = { - .start = token->start, - .end = arguments->block != NULL ? arguments->block->base.location.end : token->end - }, - }, - .block = arguments->block - }; - - return node; -} - -// Allocate and initialize a new hash pattern node from an opening and closing -// token. -static yp_hash_pattern_node_t * -yp_hash_pattern_node_empty_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *closing) { - yp_hash_pattern_node_t *node = yp_alloc(parser, sizeof(yp_hash_pattern_node_t)); - - *node = (yp_hash_pattern_node_t) { - { - .type = YP_NODE_HASH_PATTERN_NODE, - .location = { - .start = opening->start, - .end = closing->end - }, - }, - .constant = NULL, - .kwrest = NULL, - .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), - .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) - }; - - yp_node_list_init(&node->assocs); - - return node; -} - -// Allocate and initialize a new hash pattern node. -static yp_hash_pattern_node_t * -yp_hash_pattern_node_node_list_create(yp_parser_t *parser, yp_node_list_t *assocs) { - yp_hash_pattern_node_t *node = yp_alloc(parser, sizeof(yp_hash_pattern_node_t)); - - *node = (yp_hash_pattern_node_t) { - { - .type = YP_NODE_HASH_PATTERN_NODE, - .location = { - .start = assocs->nodes[0]->location.start, - .end = assocs->nodes[assocs->size - 1]->location.end - }, - }, - .constant = NULL, - .kwrest = NULL - }; - - yp_node_list_init(&node->assocs); - - for (size_t index = 0; index < assocs->size; index++) { - yp_node_t *assoc = assocs->nodes[index]; - yp_node_list_append(&node->assocs, assoc); - } - - return node; -} - -// Allocate a new GlobalVariableReadNode node. -static yp_global_variable_read_node_t * -yp_global_variable_read_node_create(yp_parser_t *parser, const yp_token_t *name) { - yp_global_variable_read_node_t *node = yp_alloc(parser, sizeof(yp_global_variable_read_node_t)); - - *node = (yp_global_variable_read_node_t) { - { - .type = YP_NODE_GLOBAL_VARIABLE_READ_NODE, - .location = YP_LOCATION_TOKEN_VALUE(name), - }, - .name = *name - }; - - return node; -} - -// Allocate a new GlobalVariableWriteNode node. -static yp_global_variable_write_node_t * -yp_global_variable_write_node_create(yp_parser_t *parser, const yp_token_t *name, const yp_token_t *operator, yp_node_t *value) { - yp_global_variable_write_node_t *node = yp_alloc(parser, sizeof(yp_global_variable_write_node_t)); - - *node = (yp_global_variable_write_node_t) { - { - .type = YP_NODE_GLOBAL_VARIABLE_WRITE_NODE, - .location = { - .start = name->start, - .end = (value == NULL ? name->end : value->location.end) - }, - }, - .name = *name, - .operator = *operator, - .value = value - }; - - return node; -} - -// Allocate a new HashNode node. -static yp_hash_node_t * -yp_hash_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *closing) { - assert(opening != NULL); - assert(closing != NULL); - yp_hash_node_t *node = yp_alloc(parser, sizeof(yp_hash_node_t)); - - *node = (yp_hash_node_t) { - { - .type = YP_NODE_HASH_NODE, - .location = { - .start = opening->start, - .end = closing->end - }, - }, - .opening = *opening, - .closing = *closing - }; - - yp_node_list_init(&node->elements); - return node; -} - -static inline void -yp_hash_node_elements_append(yp_hash_node_t *hash, yp_node_t *element) { - yp_node_list_append(&hash->elements, element); - if ((hash->opening.type == YP_TOKEN_NOT_PROVIDED) && (hash->elements.size == 1)) { - hash->base.location.start = element->location.start; - } - hash->base.location.end = element->location.end; -} - -// Allocate a new IfNode node. -static yp_if_node_t * -yp_if_node_create(yp_parser_t *parser, - const yp_token_t *if_keyword, - yp_node_t *predicate, - yp_statements_node_t *statements, - yp_node_t *consequent, - const yp_token_t *end_keyword -) { - yp_if_node_t *node = yp_alloc(parser, sizeof(yp_if_node_t)); - - const char *end; - if (end_keyword->type != YP_TOKEN_NOT_PROVIDED) { - end = end_keyword->end; - } else if (consequent != NULL) { - end = consequent->location.end; - } else if ((statements != NULL) && (statements->body.size != 0)) { - end = statements->base.location.end; - } else { - end = predicate->location.end; - } - - *node = (yp_if_node_t) { - { - .type = YP_NODE_IF_NODE, - .location = { - .start = if_keyword->start, - .end = end - }, - }, - .if_keyword = *if_keyword, - .predicate = predicate, - .statements = statements, - .consequent = consequent, - .end_keyword = *end_keyword - }; - - return node; -} - -// Allocate and initialize new IfNode node in the modifier form. -static yp_if_node_t * -yp_if_node_modifier_create(yp_parser_t *parser, yp_node_t *statement, const yp_token_t *if_keyword, yp_node_t *predicate) { - yp_if_node_t *node = yp_alloc(parser, sizeof(yp_if_node_t)); - - yp_statements_node_t *statements = yp_statements_node_create(parser); - yp_statements_node_body_append(statements, statement); - - *node = (yp_if_node_t) { - { - .type = YP_NODE_IF_NODE, - .location = { - .start = statement->location.start, - .end = predicate->location.end - }, - }, - .if_keyword = *if_keyword, - .predicate = predicate, - .statements = statements, - .consequent = NULL, - .end_keyword = not_provided(parser) - }; - - return node; -} - -// Allocate and initialize an if node from a ternary expression. -static yp_if_node_t * -yp_if_node_ternary_create(yp_parser_t *parser, yp_node_t *predicate, const yp_token_t *question_mark, yp_node_t *true_expression, const yp_token_t *colon, yp_node_t *false_expression) { - yp_statements_node_t *if_statements = yp_statements_node_create(parser); - yp_statements_node_body_append(if_statements, true_expression); - - yp_statements_node_t *else_statements = yp_statements_node_create(parser); - yp_statements_node_body_append(else_statements, false_expression); - - yp_token_t end_keyword = not_provided(parser); - yp_else_node_t *else_node = yp_else_node_create(parser, colon, else_statements, &end_keyword); - - return yp_if_node_create(parser, question_mark, predicate, if_statements, (yp_node_t *)else_node, &end_keyword); -} - -// Allocate and initialize a new IntegerNode node. -static yp_integer_node_t * -yp_integer_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_INTEGER); - yp_integer_node_t *node = yp_alloc(parser, sizeof(yp_integer_node_t)); - *node = (yp_integer_node_t) {{ .type = YP_NODE_INTEGER_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate and initialize a new RationalNode node. -static yp_rational_node_t * -yp_rational_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_RATIONAL_NUMBER); - assert(parser->lex_modes.current->mode == YP_LEX_NUMERIC); - - yp_node_t *numeric_node; - yp_token_t numeric_token = { - .type = parser->lex_modes.current->as.numeric.type, - .start = parser->lex_modes.current->as.numeric.start, - .end = parser->lex_modes.current->as.numeric.end - }; - switch (parser->lex_modes.current->as.numeric.type) { - case YP_TOKEN_INTEGER: { - lex_mode_pop(parser); - numeric_node = (yp_node_t *)yp_integer_node_create(parser, &numeric_token); - break; - } - case YP_TOKEN_FLOAT: { - lex_mode_pop(parser); - numeric_node = (yp_node_t *)yp_float_node_create(parser, &numeric_token); - break; - } - default: { - lex_mode_pop(parser); - numeric_node = (yp_node_t *)yp_missing_node_create(parser, numeric_token.start, numeric_token.end); - (void)numeric_node; // Suppress clang-analyzer-deadcode.DeadStores warning - assert(false && "unreachable"); - } - } - - yp_rational_node_t *node = yp_alloc(parser, sizeof(yp_rational_node_t)); - - *node = (yp_rational_node_t) { - { .type = YP_NODE_RATIONAL_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }, - .numeric = numeric_node, - }; - assert(parser->lex_modes.current->mode != YP_LEX_NUMERIC); - return node; -} - -// Allocate and initialize a new ImaginaryNode node. -static yp_imaginary_node_t * -yp_imaginary_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_IMAGINARY_NUMBER); - assert(parser->lex_modes.current->mode == YP_LEX_NUMERIC); - - yp_node_t *numeric_node; - yp_token_t numeric_token = { - .type = parser->lex_modes.current->as.numeric.type, - .start = parser->lex_modes.current->as.numeric.start, - .end = parser->lex_modes.current->as.numeric.end - }; - switch (parser->lex_modes.current->as.numeric.type) { - case YP_TOKEN_INTEGER: { - lex_mode_pop(parser); - numeric_node = (yp_node_t *)yp_integer_node_create(parser, &numeric_token); - break; - } - case YP_TOKEN_FLOAT: { - lex_mode_pop(parser); - numeric_node = (yp_node_t *)yp_float_node_create(parser, &numeric_token); - break; - } - case YP_TOKEN_RATIONAL_NUMBER: { - lex_mode_pop(parser); - numeric_node = (yp_node_t *)yp_rational_node_create(parser, &numeric_token); - break; - } - default: { - lex_mode_pop(parser); - numeric_node = (yp_node_t *)yp_missing_node_create(parser, numeric_token.start, numeric_token.end); - (void)numeric_node; // Suppress clang-analyzer-deadcode.DeadStores warning - assert(false && "unreachable"); - } - } - - yp_imaginary_node_t *node = yp_alloc(parser, sizeof(yp_imaginary_node_t)); - - *node = (yp_imaginary_node_t) { - { .type = YP_NODE_IMAGINARY_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }, - .numeric = numeric_node - }; - assert(parser->lex_modes.current->mode != YP_LEX_NUMERIC); - return node; -} - -// Allocate and initialize a new InNode node. -static yp_in_node_t * -yp_in_node_create(yp_parser_t *parser, yp_node_t *pattern, yp_statements_node_t *statements, const yp_token_t *in_keyword, const yp_token_t *then_keyword) { - yp_in_node_t *node = yp_alloc(parser, sizeof(yp_in_node_t)); - - const char *end; - if (then_keyword->type != YP_TOKEN_NOT_PROVIDED) { - end = then_keyword->end; - } else if (statements != NULL) { - end = statements->base.location.end; - } else { - end = pattern->location.end; - } - - *node = (yp_in_node_t) { - { - .type = YP_NODE_IN_NODE, - .location = { - .start = in_keyword->start, - .end = end - }, - }, - .pattern = pattern, - .statements = statements, - .in_loc = YP_LOCATION_TOKEN_VALUE(in_keyword), - .then_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(then_keyword) - }; - - return node; -} - -// Allocate and initialize a new InstanceVariableReadNode node. -static yp_instance_variable_read_node_t * -yp_instance_variable_read_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_INSTANCE_VARIABLE); - yp_instance_variable_read_node_t *node = yp_alloc(parser, sizeof(yp_instance_variable_read_node_t)); - - *node = (yp_instance_variable_read_node_t) {{ - .type = YP_NODE_INSTANCE_VARIABLE_READ_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) - }}; - - return node; -} - -// Initialize a new InstanceVariableWriteNode node from an InstanceVariableRead node. -static yp_instance_variable_write_node_t * -yp_instance_variable_write_node_create(yp_parser_t *parser, yp_instance_variable_read_node_t *read_node, yp_token_t *operator, yp_node_t *value) { - yp_instance_variable_write_node_t *node = yp_alloc(parser, sizeof(yp_instance_variable_write_node_t)); - *node = (yp_instance_variable_write_node_t) { - { - .type = YP_NODE_INSTANCE_VARIABLE_WRITE_NODE, - .location = { - .start = read_node->base.location.start, - .end = value == NULL ? read_node->base.location.end : value->location.end - } - }, - .name_loc = YP_LOCATION_NODE_BASE_VALUE(read_node), - .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), - .value = value - }; - - return node; -} - -// Allocate a new InterpolatedRegularExpressionNode node. -static yp_interpolated_regular_expression_node_t * -yp_interpolated_regular_expression_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *closing) { - yp_interpolated_regular_expression_node_t *node = yp_alloc(parser, sizeof(yp_interpolated_regular_expression_node_t)); - - *node = (yp_interpolated_regular_expression_node_t) { - { - .type = YP_NODE_INTERPOLATED_REGULAR_EXPRESSION_NODE, - .location = { - .start = opening->start, - .end = closing->end - }, - }, - .opening = *opening, - .closing = *closing - }; - - yp_node_list_init(&node->parts); - return node; -} - -static inline void -yp_interpolated_regular_expression_node_append(yp_interpolated_regular_expression_node_t *node, yp_node_t *part) { - yp_node_list_append(&node->parts, part); - node->base.location.end = part->location.end; -} - -static inline void -yp_interpolated_regular_expression_node_closing_set(yp_interpolated_regular_expression_node_t *node, const yp_token_t *closing) { - node->closing = *closing; - node->base.location.end = closing->end; -} - -// Allocate and initialize a new InterpolatedStringNode node. -static yp_interpolated_string_node_t * -yp_interpolated_string_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_node_list_t *parts, const yp_token_t *closing) { - yp_interpolated_string_node_t *node = yp_alloc(parser, sizeof(yp_interpolated_string_node_t)); - - *node = (yp_interpolated_string_node_t) { - { - .type = YP_NODE_INTERPOLATED_STRING_NODE, - .location = { - .start = opening->start, - .end = closing->end, - }, - }, - .opening = *opening, - .closing = *closing - }; - - if (parts == NULL) { - yp_node_list_init(&node->parts); - } else { - node->parts = *parts; - } - - return node; -} - -// Append a part to an InterpolatedStringNode node. -static inline void -yp_interpolated_string_node_append(yp_interpolated_string_node_t *node, yp_node_t *part) { - yp_node_list_append(&node->parts, part); - node->base.location.end = part->location.end; -} - -// Set the closing token of the given InterpolatedStringNode node. -static void -yp_interpolated_string_node_closing_set(yp_interpolated_string_node_t *node, const yp_token_t *closing) { - node->closing = *closing; - node->base.location.end = closing->end; -} - -// Allocate and initialize a new InterpolatedSymbolNode node. -static yp_interpolated_symbol_node_t * -yp_interpolated_symbol_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_node_list_t *parts, const yp_token_t *closing) { - yp_interpolated_symbol_node_t *node = yp_alloc(parser, sizeof(yp_interpolated_symbol_node_t)); - - *node = (yp_interpolated_symbol_node_t) { - { - .type = YP_NODE_INTERPOLATED_SYMBOL_NODE, - .location = { - .start = opening->start, - .end = closing->end, - }, - }, - .opening = *opening, - .closing = *closing - }; - - if (parts == NULL) { - yp_node_list_init(&node->parts); - } else { - node->parts = *parts; - } - - return node; -} - -static inline void -yp_interpolated_symbol_node_append(yp_interpolated_symbol_node_t *node, yp_node_t *part) { - yp_node_list_append(&node->parts, part); - node->base.location.end = part->location.end; -} - -static inline void -yp_interpolated_symbol_node_closing_set(yp_interpolated_symbol_node_t *node, const yp_token_t *closing) { - node->closing = *closing; - node->base.location.end = closing->end; -} - -// Allocate a new InterpolatedXStringNode node. -static yp_interpolated_x_string_node_t * -yp_interpolated_xstring_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *closing) { - yp_interpolated_x_string_node_t *node = yp_alloc(parser, sizeof(yp_interpolated_x_string_node_t)); - - *node = (yp_interpolated_x_string_node_t) { - { - .type = YP_NODE_INTERPOLATED_X_STRING_NODE, - .location = { - .start = opening->start, - .end = closing->end - }, - }, - .opening = *opening, - .closing = *closing - }; - - yp_node_list_init(&node->parts); - return node; -} - -static inline void -yp_interpolated_xstring_node_append(yp_interpolated_x_string_node_t *node, yp_node_t *part) { - yp_node_list_append(&node->parts, part); - node->base.location.end = part->location.end; -} - -static inline void -yp_interpolated_xstring_node_closing_set(yp_interpolated_x_string_node_t *node, const yp_token_t *closing) { - node->closing = *closing; - node->base.location.end = closing->end; -} - -// Allocate a new KeywordHashNode node. -static yp_keyword_hash_node_t * -yp_keyword_hash_node_create(yp_parser_t *parser) { - yp_keyword_hash_node_t *node = yp_alloc(parser, sizeof(yp_keyword_hash_node_t)); - - *node = (yp_keyword_hash_node_t) { - .base = { - .type = YP_NODE_KEYWORD_HASH_NODE, - .location = { - .start = NULL, - .end = NULL - }, - } - }; - - yp_node_list_init(&node->elements); - return node; -} - -// Append an element to a KeywordHashNode node. -static void -yp_keyword_hash_node_elements_append(yp_keyword_hash_node_t *hash, yp_node_t *element) { - yp_node_list_append(&hash->elements, element); - if (hash->base.location.start == NULL) { - hash->base.location.start = element->location.start; - } - hash->base.location.end = element->location.end; -} - -// Allocate a new KeywordParameterNode node. -static yp_keyword_parameter_node_t * -yp_keyword_parameter_node_create(yp_parser_t *parser, const yp_token_t *name, yp_node_t *value) { - yp_keyword_parameter_node_t *node = yp_alloc(parser, sizeof(yp_keyword_parameter_node_t)); - - *node = (yp_keyword_parameter_node_t) { - { - .type = YP_NODE_KEYWORD_PARAMETER_NODE, - .location = { - .start = name->start, - .end = value == NULL ? name->end : value->location.end - }, - }, - .name = *name, - .value = value - }; - - return node; -} - -// Allocate a new KeywordRestParameterNode node. -static yp_keyword_rest_parameter_node_t * -yp_keyword_rest_parameter_node_create(yp_parser_t *parser, const yp_token_t *operator, const yp_token_t *name) { - yp_keyword_rest_parameter_node_t *node = yp_alloc(parser, sizeof(yp_keyword_rest_parameter_node_t)); - - *node = (yp_keyword_rest_parameter_node_t) { - { - .type = YP_NODE_KEYWORD_REST_PARAMETER_NODE, - .location = { - .start = operator->start, - .end = (name->type == YP_TOKEN_NOT_PROVIDED ? operator->end : name->end) - }, - }, - .operator = *operator, - .name = *name - }; - - return node; -} - -// Allocate a new LambdaNode node. -static yp_lambda_node_t * -yp_lambda_node_create( - yp_parser_t *parser, - yp_token_list_t *locals, - const yp_token_t *opening, - yp_block_parameters_node_t *parameters, - yp_node_t *statements -) { - yp_lambda_node_t *node = yp_alloc(parser, sizeof(yp_lambda_node_t)); - - const char *end; - if (statements != NULL) { - end = statements->location.end; - } else if (parameters != NULL) { - end = parameters->base.location.end; - } else { - end = opening->end; - } - - *node = (yp_lambda_node_t) { - { - .type = YP_NODE_LAMBDA_NODE, - .location = { - .start = opening->start, - .end = end - }, - }, - .locals = *locals, - .opening = *opening, - .parameters = parameters, - .statements = statements - }; - - return node; -} - -// Allocate a new LocalVariableReadNode node. -static yp_local_variable_read_node_t * -yp_local_variable_read_node_create(yp_parser_t *parser, const yp_token_t *name, uint32_t depth) { - yp_local_variable_read_node_t *node = yp_alloc(parser, sizeof(yp_local_variable_read_node_t)); - - *node = (yp_local_variable_read_node_t) { - { - .type = YP_NODE_LOCAL_VARIABLE_READ_NODE, - .location = YP_LOCATION_TOKEN_VALUE(name) - }, - .depth = depth - }; - - return node; -} - -// Allocate and initialize a new LocalVariableWriteNode node. -static yp_local_variable_write_node_t * -yp_local_variable_write_node_create(yp_parser_t *parser, const yp_location_t *name_loc, yp_node_t *value, const yp_token_t *operator, uint32_t depth) { - yp_local_variable_write_node_t *node = yp_alloc(parser, sizeof(yp_local_variable_write_node_t)); - - *node = (yp_local_variable_write_node_t) { - { - .type = YP_NODE_LOCAL_VARIABLE_WRITE_NODE, - .location = { - .start = name_loc->start, - .end = value == NULL ? name_loc->end : value->location.end - } - }, - .name_loc = *name_loc, - .value = value, - .operator_loc = YP_OPTIONAL_LOCATION_TOKEN_VALUE(operator), - .depth = depth - }; - - return node; -} - -// Allocate and initialize a new LocalVariableWriteNode node without an operator or target. -static yp_local_variable_write_node_t * -yp_local_variable_target_node_create(yp_parser_t *parser, const yp_token_t *name) { - yp_local_variable_write_node_t *node = yp_alloc(parser, sizeof(yp_local_variable_write_node_t)); - - *node = (yp_local_variable_write_node_t) { - { - .type = YP_NODE_LOCAL_VARIABLE_WRITE_NODE, - .location = YP_LOCATION_TOKEN_VALUE(name) - }, - .name_loc = YP_LOCATION_TOKEN_VALUE(name), - .value = NULL, - .operator_loc = { .start = NULL, .end = NULL } - }; - - return node; -} - -// Allocate and initialize a new MatchPredicateNode node. -static yp_match_predicate_node_t * -yp_match_predicate_node_create(yp_parser_t *parser, yp_node_t *value, yp_node_t *pattern, const yp_token_t *operator) { - yp_match_predicate_node_t *node = yp_alloc(parser, sizeof(yp_match_predicate_node_t)); - - *node = (yp_match_predicate_node_t) { - { - .type = YP_NODE_MATCH_PREDICATE_NODE, - .location = { - .start = value->location.start, - .end = pattern->location.end - } - }, - .value = value, - .pattern = pattern, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate and initialize a new MatchRequiredNode node. -static yp_match_required_node_t * -yp_match_required_node_create(yp_parser_t *parser, yp_node_t *value, yp_node_t *pattern, const yp_token_t *operator) { - yp_match_required_node_t *node = yp_alloc(parser, sizeof(yp_match_required_node_t)); - - *node = (yp_match_required_node_t) { - { - .type = YP_NODE_MATCH_REQUIRED_NODE, - .location = { - .start = value->location.start, - .end = pattern->location.end - } - }, - .value = value, - .pattern = pattern, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate a new ModuleNode node. -static yp_module_node_t * -yp_module_node_create(yp_parser_t *parser, yp_token_list_t *locals, const yp_token_t *module_keyword, yp_node_t *constant_path, yp_node_t *statements, const yp_token_t *end_keyword) { - yp_module_node_t *node = yp_alloc(parser, sizeof(yp_module_node_t)); - - *node = (yp_module_node_t) { - { - .type = YP_NODE_MODULE_NODE, - .location = { - .start = module_keyword->start, - .end = end_keyword->end - } - }, - .locals = *locals, - .module_keyword = *module_keyword, - .constant_path = constant_path, - .statements = statements, - .end_keyword = *end_keyword - }; - - return node; -} - -// Allocate a new MultiWriteNode node. -static yp_multi_write_node_t * -yp_multi_write_node_create(yp_parser_t *parser, const yp_token_t *operator, yp_node_t *value, const yp_location_t *lparen_loc, const yp_location_t *rparen_loc) { - yp_multi_write_node_t *node = yp_alloc(parser, sizeof(yp_multi_write_node_t)); - - *node = (yp_multi_write_node_t) { - { - .type = YP_NODE_MULTI_WRITE_NODE, - .location = { .start = NULL, .end = NULL }, - }, - .operator = *operator, - .value = value, - .lparen_loc = *lparen_loc, - .rparen_loc = *rparen_loc - }; - - yp_node_list_init(&node->targets); - return node; -} - -// Append a target to a MultiWriteNode node. -static void -yp_multi_write_node_targets_append(yp_multi_write_node_t *node, yp_node_t *target) { - yp_node_list_append(&node->targets, target); - - if (node->base.location.start == NULL || (node->base.location.start > target->location.start)) { - node->base.location.start = target->location.start; - } - - if (node->base.location.end == NULL || (node->base.location.end < target->location.end)) { - node->base.location.end = target->location.end; - } -} - -// Allocate and initialize a new NextNode node. -static yp_next_node_t * -yp_next_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_arguments_node_t *arguments) { - assert(keyword->type == YP_TOKEN_KEYWORD_NEXT); - yp_next_node_t *node = yp_alloc(parser, sizeof(yp_next_node_t)); - - *node = (yp_next_node_t) { - { - .type = YP_NODE_NEXT_NODE, - .location = { - .start = keyword->start, - .end = (arguments == NULL ? keyword->end : arguments->base.location.end) - } - }, - .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), - .arguments = arguments - }; - - return node; -} - -// Allocate and initialize a new NilNode node. -static yp_nil_node_t * -yp_nil_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_KEYWORD_NIL); - yp_nil_node_t *node = yp_alloc(parser, sizeof(yp_nil_node_t)); - - *node = (yp_nil_node_t) {{ .type = YP_NODE_NIL_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate and initialize a new NoKeywordsParameterNode node. -static yp_no_keywords_parameter_node_t * -yp_no_keywords_parameter_node_create(yp_parser_t *parser, const yp_token_t *operator, const yp_token_t *keyword) { - assert(operator->type == YP_TOKEN_USTAR_STAR); - assert(keyword->type == YP_TOKEN_KEYWORD_NIL); - yp_no_keywords_parameter_node_t *node = yp_alloc(parser, sizeof(yp_no_keywords_parameter_node_t)); - - *node = (yp_no_keywords_parameter_node_t) { - { - .type = YP_NODE_NO_KEYWORDS_PARAMETER_NODE, - .location = { - .start = operator->start, - .end = keyword->end - } - }, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), - .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword) - }; - - return node; -} - -// Allocate and initialize a new OperatorAndAssignmentNode node. -static yp_operator_and_assignment_node_t * -yp_operator_and_assignment_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { - assert(operator->type == YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL); - yp_operator_and_assignment_node_t *node = yp_alloc(parser, sizeof(yp_operator_and_assignment_node_t)); - - *node = (yp_operator_and_assignment_node_t) { - { - .type = YP_NODE_OPERATOR_AND_ASSIGNMENT_NODE, - .location = { - .start = target->location.start, - .end = value->location.end - } - }, - .target = target, - .value = value, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate a new OperatorAssignmentNode node. -static yp_operator_assignment_node_t * -yp_operator_assignment_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { - yp_operator_assignment_node_t *node = yp_alloc(parser, sizeof(yp_operator_assignment_node_t)); - - *node = (yp_operator_assignment_node_t) { - { - .type = YP_NODE_OPERATOR_ASSIGNMENT_NODE, - .location = { - .start = target->location.start, - .end = value->location.end - } - }, - .target = target, - .operator = *operator, - .value = value - }; - - return node; -} - -// Allocate and initialize a new OperatorOrAssignmentNode node. -static yp_operator_or_assignment_node_t * -yp_operator_or_assignment_node_create(yp_parser_t *parser, yp_node_t *target, const yp_token_t *operator, yp_node_t *value) { - assert(operator->type == YP_TOKEN_PIPE_PIPE_EQUAL); - yp_operator_or_assignment_node_t *node = yp_alloc(parser, sizeof(yp_operator_or_assignment_node_t)); - - *node = (yp_operator_or_assignment_node_t) { - { - .type = YP_NODE_OPERATOR_OR_ASSIGNMENT_NODE, - .location = { - .start = target->location.start, - .end = value->location.end - } - }, - .target = target, - .value = value, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate a new OptionalParameterNode node. -static yp_optional_parameter_node_t * -yp_optional_parameter_node_create(yp_parser_t *parser, const yp_token_t *name, const yp_token_t *equal_operator, yp_node_t *value) { - yp_optional_parameter_node_t *node = yp_alloc(parser, sizeof(yp_optional_parameter_node_t)); - - *node = (yp_optional_parameter_node_t) { - { - .type = YP_NODE_OPTIONAL_PARAMETER_NODE, - .location = { - .start = name->start, - .end = value->location.end - } - }, - .name = *name, - .equal_operator = *equal_operator, - .value = value - }; - - return node; -} - -// Allocate and initialize a new OrNode node. -static yp_or_node_t * -yp_or_node_create(yp_parser_t *parser, yp_node_t *left, const yp_token_t *operator, yp_node_t *right) { - yp_or_node_t *node = yp_alloc(parser, sizeof(yp_or_node_t)); - - *node = (yp_or_node_t) { - { - .type = YP_NODE_OR_NODE, - .location = { - .start = left->location.start, - .end = right->location.end - } - }, - .left = left, - .right = right, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate and initialize a new ParametersNode node. -static yp_parameters_node_t * -yp_parameters_node_create(yp_parser_t *parser) { - yp_parameters_node_t *node = yp_alloc(parser, sizeof(yp_parameters_node_t)); - - *node = (yp_parameters_node_t) { - { - .type = YP_NODE_PARAMETERS_NODE, - .location = { .start = NULL, .end = NULL }, - }, - .rest = NULL, - .keyword_rest = NULL, - .block = NULL - }; - - yp_node_list_init(&node->requireds); - yp_node_list_init(&node->optionals); - yp_node_list_init(&node->posts); - yp_node_list_init(&node->keywords); - - return node; -} - -// Set the location properly for the parameters node. -static void -yp_parameters_node_location_set(yp_parameters_node_t *params, yp_node_t *param) { - if (params->base.location.start == NULL) { - params->base.location.start = param->location.start; - } else { - params->base.location.start = params->base.location.start < param->location.start ? params->base.location.start : param->location.start; - } - - if (params->base.location.end == NULL) { - params->base.location.end = param->location.end; - } else { - params->base.location.end = params->base.location.end > param->location.end ? params->base.location.end : param->location.end; - } -} - -// Append a required parameter to a ParametersNode node. -static void -yp_parameters_node_requireds_append(yp_parameters_node_t *params, yp_node_t *param) { - yp_parameters_node_location_set(params, param); - yp_node_list_append(¶ms->requireds, param); -} - -// Append an optional parameter to a ParametersNode node. -static void -yp_parameters_node_optionals_append(yp_parameters_node_t *params, yp_optional_parameter_node_t *param) { - yp_parameters_node_location_set(params, (yp_node_t *) param); - yp_node_list_append(¶ms->optionals, (yp_node_t *) param); -} - -// Append a post optional arguments parameter to a ParametersNode node. -static void -yp_parameters_node_posts_append(yp_parameters_node_t *params, yp_node_t *param) { - yp_parameters_node_location_set(params, param); - yp_node_list_append(¶ms->posts, param); -} - -// Set the rest parameter on a ParametersNode node. -static void -yp_parameters_node_rest_set(yp_parameters_node_t *params, yp_rest_parameter_node_t *param) { - yp_parameters_node_location_set(params, (yp_node_t *) param); - params->rest = param; -} - -// Append a keyword parameter to a ParametersNode node. -static void -yp_parameters_node_keywords_append(yp_parameters_node_t *params, yp_node_t *param) { - yp_parameters_node_location_set(params, param); - yp_node_list_append(¶ms->keywords, param); -} - -// Set the keyword rest parameter on a ParametersNode node. -static void -yp_parameters_node_keyword_rest_set(yp_parameters_node_t *params, yp_node_t *param) { - yp_parameters_node_location_set(params, param); - params->keyword_rest = param; -} - -// Set the block parameter on a ParametersNode node. -static void -yp_parameters_node_block_set(yp_parameters_node_t *params, yp_block_parameter_node_t *param) { - yp_parameters_node_location_set(params, (yp_node_t *) param); - params->block = param; -} - -// Allocate a new ProgramNode node. -static yp_program_node_t * -yp_program_node_create(yp_parser_t *parser, yp_token_list_t *locals, yp_statements_node_t *statements) { - yp_program_node_t *node = yp_alloc(parser, sizeof(yp_program_node_t)); - - *node = (yp_program_node_t) { - { - .type = YP_NODE_PROGRAM_NODE, - .location = { - .start = statements == NULL ? parser->start : statements->base.location.start, - .end = statements == NULL ? parser->end : statements->base.location.end - } - }, - .locals = *locals, - .statements = statements - }; - - return node; -} - -// Allocate and initialize new ParenthesesNode node. -static yp_parentheses_node_t * -yp_parentheses_node_create(yp_parser_t *parser, const yp_token_t *opening, yp_node_t *statements, const yp_token_t *closing) { - yp_parentheses_node_t *node = yp_alloc(parser, sizeof(yp_parentheses_node_t)); - - *node = (yp_parentheses_node_t) { - { - .type = YP_NODE_PARENTHESES_NODE, - .location = { - .start = opening->start, - .end = closing->end - } - }, - .statements = statements, - .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), - .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) - }; - - return node; -} - -// Allocate and initialize a new PinnedExpressionNode node. -static yp_pinned_expression_node_t * -yp_pinned_expression_node_create(yp_parser_t *parser, yp_node_t *expression, const yp_token_t *operator, const yp_token_t *lparen, const yp_token_t *rparen) { - yp_pinned_expression_node_t *node = yp_alloc(parser, sizeof(yp_pinned_expression_node_t)); - - *node = (yp_pinned_expression_node_t) { - { - .type = YP_NODE_PINNED_EXPRESSION_NODE, - .location = { - .start = operator->start, - .end = rparen->end - } - }, - .expression = expression, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), - .lparen_loc = YP_LOCATION_TOKEN_VALUE(lparen), - .rparen_loc = YP_LOCATION_TOKEN_VALUE(rparen) - }; - - return node; -} - -// Allocate and initialize a new PinnedVariableNode node. -static yp_pinned_variable_node_t * -yp_pinned_variable_node_create(yp_parser_t *parser, const yp_token_t *operator, yp_node_t *variable) { - yp_pinned_variable_node_t *node = yp_alloc(parser, sizeof(yp_pinned_variable_node_t)); - - *node = (yp_pinned_variable_node_t) { - { - .type = YP_NODE_PINNED_VARIABLE_NODE, - .location = { - .start = operator->start, - .end = variable->location.end - } - }, - .variable = variable, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator) - }; - - return node; -} - -// Allocate and initialize a new PostExecutionNode node. -static yp_post_execution_node_t * -yp_post_execution_node_create(yp_parser_t *parser, const yp_token_t *keyword, const yp_token_t *opening, yp_statements_node_t *statements, const yp_token_t *closing) { - yp_post_execution_node_t *node = yp_alloc(parser, sizeof(yp_post_execution_node_t)); - - *node = (yp_post_execution_node_t) { - { - .type = YP_NODE_POST_EXECUTION_NODE, - .location = { - .start = keyword->start, - .end = closing->end - } - }, - .statements = statements, - .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), - .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), - .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) - }; - - return node; -} - -// Allocate and initialize a new PreExecutionNode node. -static yp_pre_execution_node_t * -yp_pre_execution_node_create(yp_parser_t *parser, const yp_token_t *keyword, const yp_token_t *opening, yp_statements_node_t *statements, const yp_token_t *closing) { - yp_pre_execution_node_t *node = yp_alloc(parser, sizeof(yp_pre_execution_node_t)); - - *node = (yp_pre_execution_node_t) { - { - .type = YP_NODE_PRE_EXECUTION_NODE, - .location = { - .start = keyword->start, - .end = closing->end - } - }, - .statements = statements, - .keyword_loc = YP_LOCATION_TOKEN_VALUE(keyword), - .opening_loc = YP_LOCATION_TOKEN_VALUE(opening), - .closing_loc = YP_LOCATION_TOKEN_VALUE(closing) - }; - - return node; -} - -// Allocate and initialize new RangeNode node. -static yp_range_node_t * -yp_range_node_create(yp_parser_t *parser, yp_node_t *left, const yp_token_t *operator, yp_node_t *right) { - yp_range_node_t *node = yp_alloc(parser, sizeof(yp_range_node_t)); - - *node = (yp_range_node_t) { - { - .type = YP_NODE_RANGE_NODE, - .location = { - .start = (left == NULL ? operator->start : left->location.start), - .end = (right == NULL ? operator->end : right->location.end) - } - }, - .left = left, - .right = right, - .operator_loc = YP_LOCATION_TOKEN_VALUE(operator), - }; - - return node; -} - -// Allocate and initialize a new RedoNode node. -static yp_redo_node_t * -yp_redo_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_KEYWORD_REDO); - yp_redo_node_t *node = yp_alloc(parser, sizeof(yp_redo_node_t)); - - *node = (yp_redo_node_t) {{ .type = YP_NODE_REDO_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate a new RegularExpressionNode node. -static yp_regular_expression_node_t * -yp_regular_expression_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing) { - yp_regular_expression_node_t *node = yp_alloc(parser, sizeof(yp_regular_expression_node_t)); - - *node = (yp_regular_expression_node_t) { - { - .type = YP_NODE_REGULAR_EXPRESSION_NODE, - .location = { - .start = opening->start, - .end = closing->end - } - }, - .opening = *opening, - .content = *content, - .closing = *closing - }; - - return node; -} - -// Allocate a new RequiredDestructuredParameterNode node. -static yp_required_destructured_parameter_node_t * -yp_required_destructured_parameter_node_create(yp_parser_t *parser, const yp_token_t *opening) { - yp_required_destructured_parameter_node_t *node = yp_alloc(parser, sizeof(yp_required_destructured_parameter_node_t)); - - *node = (yp_required_destructured_parameter_node_t) { - { - .type = YP_NODE_REQUIRED_DESTRUCTURED_PARAMETER_NODE, - .location = YP_LOCATION_TOKEN_VALUE(opening) - }, - .opening = *opening - }; - - yp_node_list_init(&node->parameters); - return node; -} - -// Append a new parameter to the given RequiredDestructuredParameterNode node. -static void -yp_required_destructured_parameter_node_append_parameter(yp_required_destructured_parameter_node_t *node, yp_node_t *parameter) { - yp_node_list_append(&node->parameters, parameter); -} - -// Set the closing token of the given RequiredDestructuredParameterNode node. -static void -yp_required_destructured_parameter_node_closing_set(yp_required_destructured_parameter_node_t *node, const yp_token_t *closing) { - node->closing = *closing; - node->base.location.end = closing->end; -} - -// Allocate a new RequiredParameterNode node. -static yp_required_parameter_node_t * -yp_required_parameter_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_MISSING || token->type == YP_TOKEN_IDENTIFIER); - yp_required_parameter_node_t *node = yp_alloc(parser, sizeof(yp_required_parameter_node_t)); - - *node = (yp_required_parameter_node_t) {{ .type = YP_NODE_REQUIRED_PARAMETER_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate a new RescueModifierNode node. -static yp_rescue_modifier_node_t * -yp_rescue_modifier_node_create(yp_parser_t *parser, yp_node_t *expression, const yp_token_t *rescue_keyword, yp_node_t *rescue_expression) { - yp_rescue_modifier_node_t *node = yp_alloc(parser, sizeof(yp_rescue_modifier_node_t)); - - *node = (yp_rescue_modifier_node_t) { - { - .type = YP_NODE_RESCUE_MODIFIER_NODE, - .location = { - .start = expression->location.start, - .end = rescue_expression->location.end - } - }, - .expression = expression, - .rescue_keyword = *rescue_keyword, - .rescue_expression = rescue_expression - }; - - return node; -} - -// Allocate and initiliaze a new RescueNode node. -static yp_rescue_node_t * -yp_rescue_node_create(yp_parser_t *parser, const yp_token_t *rescue_keyword) { - yp_rescue_node_t *node = yp_alloc(parser, sizeof(yp_rescue_node_t)); - - *node = (yp_rescue_node_t) { - { - .type = YP_NODE_RESCUE_NODE, - .location = { - .start = rescue_keyword->start, - .end = rescue_keyword->end - } - }, - .rescue_keyword = *rescue_keyword, - .equal_greater = YP_TOKEN_NOT_PROVIDED_VALUE(parser), - .exception = NULL, - .statements = NULL, - .consequent = NULL - }; - - yp_node_list_init(&node->exceptions); - return node; -} - -// Set the exception of a rescue node, and update the location of the node. -static void -yp_rescue_node_exception_set(yp_rescue_node_t *node, yp_node_t *exception) { - node->exception = exception; - node->base.location.end = exception->location.end; -} - -// Set the statements of a rescue node, and update the location of the node. -static void -yp_rescue_node_statements_set(yp_rescue_node_t *node, yp_statements_node_t *statements) { - node->statements = statements; - if ((statements != NULL) && (statements->body.size > 0)) { - node->base.location.end = statements->base.location.end; - } -} - -// Set the consequent of a rescue node, and update the location. -static void -yp_rescue_node_consequent_set(yp_rescue_node_t *node, yp_rescue_node_t *consequent) { - node->consequent = consequent; - node->base.location.end = consequent->base.location.end; -} - -// Append an exception node to a rescue node, and update the location. -static void -yp_rescue_node_exceptions_append(yp_rescue_node_t *node, yp_node_t *exception) { - yp_node_list_append(&node->exceptions, exception); - node->base.location.end = exception->location.end; -} - -// Allocate a new RestParameterNode node. -static yp_rest_parameter_node_t * -yp_rest_parameter_node_create(yp_parser_t *parser, const yp_token_t *operator, const yp_token_t *name) { - yp_rest_parameter_node_t *node = yp_alloc(parser, sizeof(yp_rest_parameter_node_t)); - - *node = (yp_rest_parameter_node_t) { - { - .type = YP_NODE_REST_PARAMETER_NODE, - .location = { - .start = operator->start, - .end = (name->type == YP_TOKEN_NOT_PROVIDED ? operator->end : name->end) - } - }, - .operator = *operator, - .name = *name - }; - - return node; -} - -// Allocate and initialize a new RetryNode node. -static yp_retry_node_t * -yp_retry_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_KEYWORD_RETRY); - yp_retry_node_t *node = yp_alloc(parser, sizeof(yp_retry_node_t)); - - *node = (yp_retry_node_t) {{ .type = YP_NODE_RETRY_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate a new ReturnNode node. -static yp_return_node_t * -yp_return_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_arguments_node_t *arguments) { - yp_return_node_t *node = yp_alloc(parser, sizeof(yp_return_node_t)); - - *node = (yp_return_node_t) { - { - .type = YP_NODE_RETURN_NODE, - .location = { - .start = keyword->start, - .end = (arguments == NULL ? keyword->end : arguments->base.location.end) - } - }, - .keyword = *keyword, - .arguments = arguments - }; - - return node; -} - -// Allocate and initialize a new SelfNode node. -static yp_self_node_t * -yp_self_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_KEYWORD_SELF); - yp_self_node_t *node = yp_alloc(parser, sizeof(yp_self_node_t)); - - *node = (yp_self_node_t) {{ .type = YP_NODE_SELF_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate a new SingletonClassNode node. -static yp_singleton_class_node_t * -yp_singleton_class_node_create(yp_parser_t *parser, yp_token_list_t *locals, const yp_token_t *class_keyword, const yp_token_t *operator, yp_node_t *expression, yp_node_t *statements, const yp_token_t *end_keyword) { - yp_singleton_class_node_t *node = yp_alloc(parser, sizeof(yp_singleton_class_node_t)); - - *node = (yp_singleton_class_node_t) { - { - .type = YP_NODE_SINGLETON_CLASS_NODE, - .location = { - .start = class_keyword->start, - .end = end_keyword->end - } - }, - .locals = *locals, - .class_keyword = *class_keyword, - .operator = *operator, - .expression = expression, - .statements = statements, - .end_keyword = *end_keyword - }; - - return node; -} - -// Allocate and initialize a new SourceEncodingNode node. -static yp_source_encoding_node_t * -yp_source_encoding_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_KEYWORD___ENCODING__); - yp_source_encoding_node_t *node = yp_alloc(parser, sizeof(yp_source_encoding_node_t)); - - *node = (yp_source_encoding_node_t) {{ .type = YP_NODE_SOURCE_ENCODING_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate and initialize a new SourceFileNode node. -static yp_source_file_node_t* -yp_source_file_node_create(yp_parser_t *parser, const yp_token_t *file_keyword) { - yp_source_file_node_t *node = yp_alloc(parser, sizeof(yp_source_file_node_t)); - assert(file_keyword->type == YP_TOKEN_KEYWORD___FILE__); - - *node = (yp_source_file_node_t) { - { - .type = YP_NODE_SOURCE_FILE_NODE, - .location = YP_LOCATION_TOKEN_VALUE(file_keyword), - }, - .filepath = parser->filepath_string, - }; - - return node; -} - -// Allocate and initialize a new SourceLineNode node. -static yp_source_line_node_t * -yp_source_line_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_KEYWORD___LINE__); - yp_source_line_node_t *node = yp_alloc(parser, sizeof(yp_source_line_node_t)); - - *node = (yp_source_line_node_t) {{ .type = YP_NODE_SOURCE_LINE_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate a new SplatNode node. -static yp_splat_node_t * -yp_splat_node_create(yp_parser_t *parser, const yp_token_t *operator, yp_node_t *expression) { - yp_splat_node_t *node = yp_alloc(parser, sizeof(yp_splat_node_t)); - - *node = (yp_splat_node_t) { - { - .type = YP_NODE_SPLAT_NODE, - .location = { - .start = operator->start, - .end = (expression == NULL ? operator->end : expression->location.end) - } - }, - .operator = *operator, - .expression = expression - }; - - return node; -} - -// Allocate and initialize a new StatementsNode node. -static yp_statements_node_t * -yp_statements_node_create(yp_parser_t *parser) { - yp_statements_node_t *node = yp_alloc(parser, sizeof(yp_statements_node_t)); - - *node = (yp_statements_node_t) { - { - .type = YP_NODE_STATEMENTS_NODE, - .location = YP_LOCATION_NULL_VALUE(parser) - }, - .body = YP_EMPTY_NODE_LIST - }; - - return node; -} - -// Get the length of the given StatementsNode node's body. -static size_t -yp_statements_node_body_length(yp_statements_node_t *node) { - return node->body.size; -} - -// Set the location of the given StatementsNode. -static void -yp_statements_node_location_set(yp_statements_node_t *node, const char *start, const char *end) { - node->base.location = (yp_location_t) { .start = start, .end = end }; -} - -// Append a new node to the given StatementsNode node's body. -static void -yp_statements_node_body_append(yp_statements_node_t *node, yp_node_t *statement) { - if (yp_statements_node_body_length(node) == 0) { - node->base.location.start = statement->location.start; - } - - yp_node_list_append(&node->body, statement); - node->base.location.end = statement->location.end; -} - -// Allocate a new StringConcatNode node. -static yp_string_concat_node_t * -yp_string_concat_node_create(yp_parser_t *parser, yp_node_t *left, yp_node_t *right) { - yp_string_concat_node_t *node = yp_alloc(parser, sizeof(yp_string_concat_node_t)); - - *node = (yp_string_concat_node_t) { - { - .type = YP_NODE_STRING_CONCAT_NODE, - .location = { - .start = left->location.start, - .end = right->location.end - } - }, - .left = left, - .right = right - }; - - return node; -} - -// Allocate a new StringInterpolatedNode node. -static yp_string_interpolated_node_t * -yp_string_interpolated_node_create(yp_parser_t *parser, const yp_token_t *opening, yp_statements_node_t *statements, const yp_token_t *closing) { - yp_string_interpolated_node_t *node = yp_alloc(parser, sizeof(yp_string_interpolated_node_t)); - - *node = (yp_string_interpolated_node_t) { - { - .type = YP_NODE_STRING_INTERPOLATED_NODE, - .location = { - .start = opening->start, - .end = closing->end - } - }, - .opening = *opening, - .statements = statements, - .closing = *closing - }; - - return node; -} - -// Allocate a new StringNode node. -static yp_string_node_t * -yp_string_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing) { - yp_string_node_t *node = yp_alloc(parser, sizeof(yp_string_node_t)); - - *node = (yp_string_node_t) { - { - .type = YP_NODE_STRING_NODE, - .location = { - .start = (opening->type == YP_TOKEN_NOT_PROVIDED ? content->start : opening->start), - .end = (closing->type == YP_TOKEN_NOT_PROVIDED ? content->end : closing->end) - } - }, - .opening = *opening, - .content = *content, - .closing = *closing - }; - - return node; -} - -// Allocate and initialize a new SuperNode node. -static yp_super_node_t * -yp_super_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_arguments_t *arguments) { - assert(keyword->type == YP_TOKEN_KEYWORD_SUPER); - yp_super_node_t *node = yp_alloc(parser, sizeof(yp_super_node_t)); - - const char *end; - if (arguments->block != NULL) { - end = arguments->block->base.location.end; - } else if (arguments->closing.type != YP_TOKEN_NOT_PROVIDED) { - end = arguments->closing.end; - } else if (arguments->arguments != NULL) { - end = arguments->arguments->base.location.end; - } else { - assert(false && "unreachable"); - end = NULL; - } - - *node = (yp_super_node_t) { - { - .type = YP_NODE_SUPER_NODE, - .location = { - .start = keyword->start, - .end = end, - } - }, - .keyword = *keyword, - .lparen = arguments->opening, - .arguments = arguments->arguments, - .rparen = arguments->closing, - .block = arguments->block - }; - - return node; -} - -// Allocate a new SymbolNode node. -static yp_symbol_node_t * -yp_symbol_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *value, const yp_token_t *closing) { - yp_symbol_node_t *node = yp_alloc(parser, sizeof(yp_symbol_node_t)); - - *node = (yp_symbol_node_t) { - { - .type = YP_NODE_SYMBOL_NODE, - .location = { - .start = (opening->type == YP_TOKEN_NOT_PROVIDED ? value->start : opening->start), - .end = (closing->type == YP_TOKEN_NOT_PROVIDED ? value->end : closing->end) - } - }, - .opening = *opening, - .value = *value, - .closing = *closing - }; - - return node; -} - -// Allocate and initialize a new SymbolNode node from a label. -static yp_symbol_node_t * -yp_symbol_node_label_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_LABEL || token->type == YP_TOKEN_MISSING); - - yp_token_t opening = not_provided(parser); - yp_token_t label = { .type = YP_TOKEN_LABEL, .start = token->start, .end = token->end - 1 }; - yp_token_t closing = { .type = YP_TOKEN_LABEL_END, .start = label.end, .end = label.end + 1 }; - - yp_symbol_node_t *node = yp_symbol_node_create(parser, &opening, &label, &closing); - yp_unescape_manipulate_string(label.start, (size_t) (label.end - label.start), &node->unescaped, YP_UNESCAPE_ALL, &parser->error_list); - return node; -} - -// Check if the given node is a label in a hash. -static bool -yp_symbol_node_label_p(yp_node_t *node) { - return ( - (node->type == YP_NODE_SYMBOL_NODE && ((yp_symbol_node_t *) node)->closing.type == YP_TOKEN_LABEL_END) || - (node->type == YP_NODE_INTERPOLATED_SYMBOL_NODE && ((yp_interpolated_symbol_node_t *)node)->closing.type == YP_TOKEN_LABEL_END) - ); -} - -// Convert the given SymbolNode node to a StringNode node. -static yp_string_node_t * -yp_symbol_node_to_string_node(yp_parser_t *parser, yp_symbol_node_t *node) { - yp_string_node_t *new_node = yp_alloc(parser, sizeof(yp_string_node_t)); - - *new_node = (yp_string_node_t) { - { - .type = YP_NODE_STRING_NODE, - .location = node->base.location - }, - .opening = node->opening, - .content = node->value, - .closing = node->closing, - .unescaped = node->unescaped - }; - - yp_node_destroy(parser, (yp_node_t *) node); - return new_node; -} - -// Allocate and initialize a new TrueNode node. -static yp_true_node_t * -yp_true_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_KEYWORD_TRUE); - yp_true_node_t *node = yp_alloc(parser, sizeof(yp_true_node_t)); - - *node = (yp_true_node_t) {{ .type = YP_NODE_TRUE_NODE, .location = YP_LOCATION_TOKEN_VALUE(token) }}; - return node; -} - -// Allocate and initialize a new UndefNode node. -static yp_undef_node_t * -yp_undef_node_create(yp_parser_t *parser, const yp_token_t *token) { - assert(token->type == YP_TOKEN_KEYWORD_UNDEF); - yp_undef_node_t *node = yp_alloc(parser, sizeof(yp_undef_node_t)); - - *node = (yp_undef_node_t) { - { - .type = YP_NODE_UNDEF_NODE, - .location = YP_LOCATION_TOKEN_VALUE(token), - }, - .keyword_loc = YP_LOCATION_TOKEN_VALUE(token) - }; - - yp_node_list_init(&node->names); - return node; -} - -// Append a name to an undef node. -static void -yp_undef_node_append(yp_undef_node_t *node, yp_node_t *name) { - node->base.location.end = name->location.end; - yp_node_list_append(&node->names, name); -} - -// Allocate a new UnlessNode node. -static yp_unless_node_t * -yp_unless_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements) { - yp_unless_node_t *node = yp_alloc(parser, sizeof(yp_unless_node_t)); - - const char *end; - if (statements != NULL) { - end = statements->base.location.end; - } else { - end = predicate->location.end; - } - - *node = (yp_unless_node_t) { - { - .type = YP_NODE_UNLESS_NODE, - .location = { - .start = keyword->start, - .end = end - }, - }, - .keyword = *keyword, - .predicate = predicate, - .statements = statements, - .consequent = NULL, - .end_keyword = YP_TOKEN_NOT_PROVIDED_VALUE(parser) - }; - - return node; -} - -// Allocate and initialize new UnlessNode node in the modifier form. -static yp_unless_node_t * -yp_unless_node_modifier_create(yp_parser_t *parser, yp_node_t *statement, const yp_token_t *unless_keyword, yp_node_t *predicate) { - yp_unless_node_t *node = yp_alloc(parser, sizeof(yp_unless_node_t)); - - yp_statements_node_t *statements = yp_statements_node_create(parser); - yp_statements_node_body_append(statements, statement); - - *node = (yp_unless_node_t) { - { - .type = YP_NODE_UNLESS_NODE, - .location = { - .start = statement->location.start, - .end = predicate->location.end - }, - }, - .keyword = *unless_keyword, - .predicate = predicate, - .statements = statements, - .consequent = NULL, - .end_keyword = not_provided(parser) - }; - - return node; -} - -// Allocate a new UntilNode node. -static yp_until_node_t * -yp_until_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements) { - yp_until_node_t *node = yp_alloc(parser, sizeof(yp_until_node_t)); - bool has_statements = (statements != NULL) && (statements->body.size != 0); - - const char *start = NULL; - if (has_statements && (keyword->start > statements->base.location.start)) { - start = statements->base.location.start; - } else { - start = keyword->start; - } - - const char *end = NULL; - if (has_statements && (predicate->location.end < statements->base.location.end)) { - end = statements->base.location.end; - } else { - end = predicate->location.end; - } - - *node = (yp_until_node_t) { - { - .type = YP_NODE_UNTIL_NODE, - .location = { - .start = start, - .end = end, - }, - }, - .keyword = *keyword, - .predicate = predicate, - .statements = statements - }; - - return node; -} - -// Allocate and initialize a new WhenNode node. -static yp_when_node_t * -yp_when_node_create(yp_parser_t *parser, const yp_token_t *when_keyword) { - yp_when_node_t *node = yp_alloc(parser, sizeof(yp_when_node_t)); - - *node = (yp_when_node_t) { - { - .type = YP_NODE_WHEN_NODE, - .location = { - .start = when_keyword->start, - .end = NULL - } - }, - .when_keyword = *when_keyword, - .statements = NULL - }; - - yp_node_list_init(&node->conditions); - return node; -} - -// Append a new condition to a when node. -static void -yp_when_node_conditions_append(yp_when_node_t *node, yp_node_t *condition) { - node->base.location.end = condition->location.end; - yp_node_list_append(&node->conditions, condition); -} - -// Set the statements list of a when node. -static void -yp_when_node_statements_set(yp_when_node_t *node, yp_statements_node_t *statements) { - if (statements->base.location.end > node->base.location.end) { - node->base.location.end = statements->base.location.end; - } - - node->statements = statements; -} - -// Allocate a new WhileNode node. -static yp_while_node_t * -yp_while_node_create(yp_parser_t *parser, const yp_token_t *keyword, yp_node_t *predicate, yp_statements_node_t *statements) { - yp_while_node_t *node = yp_alloc(parser, sizeof(yp_while_node_t)); - - const char *start = NULL; - bool has_statements = (statements != NULL) && (statements->body.size != 0); - if (has_statements && (keyword->start > statements->base.location.start)) { - start = statements->base.location.start; - } else { - start = keyword->start; - } - - const char *end = NULL; - if (has_statements && (predicate->location.end < statements->base.location.end)) { - end = statements->base.location.end; - } else { - end = predicate->location.end; - } - - *node = (yp_while_node_t) { - { - .type = YP_NODE_WHILE_NODE, - .location = { - .start = start, - .end = end, - }, - }, - .keyword = *keyword, - .predicate = predicate, - .statements = statements - }; - - return node; -} - -// Allocate and initialize a new XStringNode node. -static yp_x_string_node_t * -yp_xstring_node_create(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing) { - yp_x_string_node_t *node = yp_alloc(parser, sizeof(yp_x_string_node_t)); - - *node = (yp_x_string_node_t) { - { - .type = YP_NODE_X_STRING_NODE, - .location = { - .start = opening->start, - .end = closing->end - }, - }, - .opening = *opening, - .content = *content, - .closing = *closing - }; - - return node; -} - -// Allocate a new YieldNode node. -static yp_yield_node_t * -yp_yield_node_create(yp_parser_t *parser, const yp_token_t *keyword, const yp_token_t *lparen, yp_arguments_node_t *arguments, const yp_token_t *rparen) { - yp_yield_node_t *node = yp_alloc(parser, sizeof(yp_yield_node_t)); - - const char *end; - if (rparen->type != YP_TOKEN_NOT_PROVIDED) { - end = rparen->end; - } else if (arguments != NULL) { - end = arguments->base.location.end; - } else if (lparen->type != YP_TOKEN_NOT_PROVIDED) { - end = lparen->end; - } else { - end = keyword->end; - } - - *node = (yp_yield_node_t) { - { - .type = YP_NODE_YIELD_NODE, - .location = { - .start = keyword->start, - .end = end - }, - }, - .keyword = *keyword, - .lparen = *lparen, - .arguments = arguments, - .rparen = *rparen - }; - - return node; -} - - -#undef YP_LOCATION_NULL_VALUE -#undef YP_LOCATION_TOKEN_VALUE -#undef YP_LOCATION_NODE_VALUE -#undef YP_LOCATION_NODE_BASE_VALUE -#undef YP_TOKEN_NOT_PROVIDED_VALUE - -/******************************************************************************/ -/* Scope-related functions */ -/******************************************************************************/ - -// Allocate and initialize a new scope. Push it onto the scope stack. -static void -yp_parser_scope_push(yp_parser_t *parser, bool top) { - yp_scope_t *scope = (yp_scope_t *) malloc(sizeof(yp_scope_t)); - *scope = (yp_scope_t) { .locals = YP_EMPTY_TOKEN_LIST, .top = top, .previous = parser->current_scope }; - parser->current_scope = scope; -} - -// Check if the current scope has a given local variables. -static int -yp_parser_local_depth(yp_parser_t *parser, yp_token_t *token) { - yp_scope_t *scope = parser->current_scope; - int depth = 0; - - while (scope != NULL) { - if (yp_token_list_includes(&scope->locals, token)) return depth; - if (scope->top) break; - - scope = scope->previous; - depth++; - } - - return -1; -} - -// Add a local variable to the current scope. -static void -yp_parser_local_add(yp_parser_t *parser, yp_token_t *token) { - if (!yp_token_list_includes(&parser->current_scope->locals, token)) { - yp_token_list_append(&parser->current_scope->locals, token); - } -} - -// Add a parameter name to the current scope and check whether the name of the parameter is unique or not. -static void -yp_parser_parameter_name_check(yp_parser_t *parser, yp_token_t *name) { - if ((name->start[0] != '_') && (yp_token_list_includes(&parser->current_scope->locals, name))) { - yp_diagnostic_list_append(&parser->error_list, name->start, name->end, "Duplicated argument name."); - } -} - -// Pop the current scope off the scope stack. -static void -yp_parser_scope_pop(yp_parser_t *parser) { - yp_scope_t *scope = parser->current_scope; - parser->current_scope = scope->previous; - free(scope); -} - -/******************************************************************************/ -/* Basic character checks */ -/******************************************************************************/ - -static inline size_t -char_is_identifier_start(yp_parser_t *parser, const char *c) { - if (*c == '_') { - return 1; - } else if (((unsigned char) *c) > 127) { - return 1; - } else { - return parser->encoding.alpha_char(c); - } -} - -static inline size_t -char_is_identifier(yp_parser_t *parser, const char *c) { - size_t width; - - if ((width = parser->encoding.alnum_char(c))) { - return width; - } else if (*c == '_') { - return 1; - } else if (((unsigned char) *c) > 127) { - return 1; - } else { - return 0; - } -} - -#define BIT(c, idx) (((c) / 32 - 1 == idx) ? (1U << ((c) % 32)) : 0) -#define PUNCT(idx) ( \ - BIT('~', idx) | BIT('*', idx) | BIT('$', idx) | BIT('?', idx) | \ - BIT('!', idx) | BIT('@', idx) | BIT('/', idx) | BIT('\\', idx) | \ - BIT(';', idx) | BIT(',', idx) | BIT('.', idx) | BIT('=', idx) | \ - BIT(':', idx) | BIT('<', idx) | BIT('>', idx) | BIT('\"', idx) | \ - BIT('&', idx) | BIT('`', idx) | BIT('\'', idx) | BIT('+', idx) | \ - BIT('0', idx)) - -const unsigned int yp_global_name_punctuation_hash[(0x7e - 0x20 + 31) / 32] = { PUNCT(0), PUNCT(1), PUNCT(2) }; - -#undef BIT -#undef PUNCT - -static inline bool -char_is_global_name_punctuation(const char c) { - const unsigned int i = (const unsigned int) c; - if (i <= 0x20 || 0x7e < i) return false; - - return (yp_global_name_punctuation_hash[(i - 0x20) / 32] >> (c % 32)) & 1; -} - -static inline bool -token_is_numbered_parameter(yp_token_t *token) { - return - (token->type == YP_TOKEN_IDENTIFIER) && - (token->end - token->start == 2) && - (token->start[0] == '_') && - (yp_char_is_decimal_digit(token->start[1])); -} - -static inline bool -token_is_setter_name(yp_token_t *token) { - return ( - (token->type == YP_TOKEN_IDENTIFIER) && - (token->end - token->start >= 2) && - (token->end[-1] == '=') - ); -} - -/******************************************************************************/ -/* Stack helpers */ -/******************************************************************************/ - -static inline void -yp_accepts_block_stack_push(yp_parser_t *parser, bool value) { - // Use the negation of the value to prevent stack overflow. - yp_state_stack_push(&parser->accepts_block_stack, !value); -} - -static inline void -yp_accepts_block_stack_pop(yp_parser_t *parser) { - yp_state_stack_pop(&parser->accepts_block_stack); -} - -static inline bool -yp_accepts_block_stack_p(yp_parser_t *parser) { - return !yp_state_stack_p(&parser->accepts_block_stack); -} - -static inline void -yp_do_loop_stack_push(yp_parser_t *parser, bool value) { - yp_state_stack_push(&parser->do_loop_stack, value); -} - -static inline void -yp_do_loop_stack_pop(yp_parser_t *parser) { - yp_state_stack_pop(&parser->do_loop_stack); -} - -static inline bool -yp_do_loop_stack_p(yp_parser_t *parser) { - return yp_state_stack_p(&parser->do_loop_stack); -} - -/******************************************************************************/ -/* Lexer check helpers */ -/******************************************************************************/ - -// Get the next character in the source starting from parser->current.end and -// adding the given offset. If that position is beyond the end of the source -// then return '\0'. -static inline char -peek_at(yp_parser_t *parser, size_t offset) { - if (parser->current.end + offset < parser->end) { - return parser->current.end[offset]; - } else { - return '\0'; - } -} - -// Get the next character in the source starting from parser->current.end. If -// that position is beyond the end of the source then return '\0'. -static inline char -peek(yp_parser_t *parser) { - if (parser->current.end < parser->end) { - return *parser->current.end; - } else { - return '\0'; - } -} - -// If the character to be read matches the given value, then returns true and -// advanced the current pointer. -static inline bool -match(yp_parser_t *parser, char value) { - if (peek(parser) == value) { - parser->current.end++; - return true; - } - return false; -} - -// Returns the incrementor character that should be used to increment the -// nesting count if one is possible. -static inline char -incrementor(const char start) { - switch (start) { - case '(': - case '[': - case '{': - case '<': - return start; - default: - return '\0'; - } -} - -// Returns the matching character that should be used to terminate a list -// beginning with the given character. -static inline char -terminator(const char start) { - switch (start) { - case '(': - return ')'; - case '[': - return ']'; - case '{': - return '}'; - case '<': - return '>'; - default: - return start; - } -} - -/******************************************************************************/ -/* Encoding-related functions */ -/******************************************************************************/ - -static yp_encoding_t yp_encoding_ascii = { - .name = "ascii", - .char_width = yp_encoding_ascii_char_width, - .alnum_char = yp_encoding_ascii_alnum_char, - .alpha_char = yp_encoding_ascii_alpha_char, - .isupper_char = yp_encoding_ascii_isupper_char -}; - -static yp_encoding_t yp_encoding_ascii_8bit = { - .name = "ascii-8bit", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_ascii_alnum_char, - .alpha_char = yp_encoding_ascii_alpha_char, - .isupper_char = yp_encoding_ascii_isupper_char, -}; - -static yp_encoding_t yp_encoding_big5 = { - .name = "big5", - .char_width = yp_encoding_big5_char_width, - .alnum_char = yp_encoding_big5_alnum_char, - .alpha_char = yp_encoding_big5_alpha_char, - .isupper_char = yp_encoding_big5_isupper_char -}; - -static yp_encoding_t yp_encoding_euc_jp = { - .name = "euc-jp", - .char_width = yp_encoding_euc_jp_char_width, - .alnum_char = yp_encoding_euc_jp_alnum_char, - .alpha_char = yp_encoding_euc_jp_alpha_char, - .isupper_char = yp_encoding_euc_jp_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_1 = { - .name = "iso-8859-1", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_1_alnum_char, - .alpha_char = yp_encoding_iso_8859_1_alpha_char, - .isupper_char = yp_encoding_iso_8859_1_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_2 = { - .name = "iso-8859-2", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_2_alnum_char, - .alpha_char = yp_encoding_iso_8859_2_alpha_char, - .isupper_char = yp_encoding_iso_8859_2_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_3 = { - .name = "iso-8859-3", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_3_alnum_char, - .alpha_char = yp_encoding_iso_8859_3_alpha_char, - .isupper_char = yp_encoding_iso_8859_3_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_4 = { - .name = "iso-8859-4", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_4_alnum_char, - .alpha_char = yp_encoding_iso_8859_4_alpha_char, - .isupper_char = yp_encoding_iso_8859_4_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_5 = { - .name = "iso-8859-5", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_5_alnum_char, - .alpha_char = yp_encoding_iso_8859_5_alpha_char, - .isupper_char = yp_encoding_iso_8859_5_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_6 = { - .name = "iso-8859-6", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_6_alnum_char, - .alpha_char = yp_encoding_iso_8859_6_alpha_char, - .isupper_char = yp_encoding_iso_8859_6_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_7 = { - .name = "iso-8859-7", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_7_alnum_char, - .alpha_char = yp_encoding_iso_8859_7_alpha_char, - .isupper_char = yp_encoding_iso_8859_7_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_8 = { - .name = "iso-8859-8", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_8_alnum_char, - .alpha_char = yp_encoding_iso_8859_8_alpha_char, - .isupper_char = yp_encoding_iso_8859_8_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_9 = { - .name = "iso-8859-9", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_9_alnum_char, - .alpha_char = yp_encoding_iso_8859_9_alpha_char, - .isupper_char = yp_encoding_iso_8859_9_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_10 = { - .name = "iso-8859-10", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_10_alnum_char, - .alpha_char = yp_encoding_iso_8859_10_alpha_char, - .isupper_char = yp_encoding_iso_8859_10_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_11 = { - .name = "iso-8859-11", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_11_alnum_char, - .alpha_char = yp_encoding_iso_8859_11_alpha_char, - .isupper_char = yp_encoding_iso_8859_11_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_13 = { - .name = "iso-8859-13", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_13_alnum_char, - .alpha_char = yp_encoding_iso_8859_13_alpha_char, - .isupper_char = yp_encoding_iso_8859_13_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_14 = { - .name = "iso-8859-14", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_14_alnum_char, - .alpha_char = yp_encoding_iso_8859_14_alpha_char, - .isupper_char = yp_encoding_iso_8859_14_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_15 = { - .name = "iso-8859-15", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_15_alnum_char, - .alpha_char = yp_encoding_iso_8859_15_alpha_char, - .isupper_char = yp_encoding_iso_8859_15_isupper_char -}; - -static yp_encoding_t yp_encoding_iso_8859_16 = { - .name = "iso-8859-16", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_iso_8859_16_alnum_char, - .alpha_char = yp_encoding_iso_8859_16_alpha_char, - .isupper_char = yp_encoding_iso_8859_16_isupper_char -}; - -static yp_encoding_t yp_encoding_shift_jis = { - .name = "shift_jis", - .char_width = yp_encoding_shift_jis_char_width, - .alnum_char = yp_encoding_shift_jis_alnum_char, - .alpha_char = yp_encoding_shift_jis_alpha_char, - .isupper_char = yp_encoding_shift_jis_isupper_char -}; - -static yp_encoding_t yp_encoding_utf_8 = { - .name = "utf-8", - .char_width = yp_encoding_utf_8_char_width, - .alnum_char = yp_encoding_utf_8_alnum_char, - .alpha_char = yp_encoding_utf_8_alpha_char, - .isupper_char = yp_encoding_utf_8_isupper_char -}; - -static yp_encoding_t yp_encoding_windows_31j = { - .name = "windows-31j", - .char_width = yp_encoding_windows_31j_char_width, - .alnum_char = yp_encoding_windows_31j_alnum_char, - .alpha_char = yp_encoding_windows_31j_alpha_char, - .isupper_char = yp_encoding_windows_31j_isupper_char -}; - -static yp_encoding_t yp_encoding_windows_1251 = { - .name = "windows-1251", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_windows_1251_alnum_char, - .alpha_char = yp_encoding_windows_1251_alpha_char, - .isupper_char = yp_encoding_windows_1251_isupper_char -}; - -static yp_encoding_t yp_encoding_windows_1252 = { - .name = "windows-1252", - .char_width = yp_encoding_single_char_width, - .alnum_char = yp_encoding_windows_1252_alnum_char, - .alpha_char = yp_encoding_windows_1252_alpha_char, - .isupper_char = yp_encoding_windows_1252_isupper_char -}; - -// Here we're going to check if this is a "magic" comment, and perform whatever -// actions are necessary for it here. -static void -parser_lex_encoding_comment(yp_parser_t *parser) { - const char *start = parser->current.start + 1; - const char *end = memchr(start, '\n', (size_t) (parser->end - start)); - if (end == NULL) end = parser->end; - - // These are the patterns we're going to match to find the encoding comment. - // This is definitely not complete or even really correct. - const char *encoding_start = NULL; - if ((encoding_start = strnstr(start, "coding:", (size_t) (end - start))) != NULL) { - encoding_start += 7; - } else if ((encoding_start = strnstr(start, "coding=", (size_t) (end - start))) != NULL) { - encoding_start += 7; - } - - // If we didn't find anything that matched our patterns, then return. Note - // that this does a _very_ poor job of actually finding the encoding, and - // there is a lot of work to do here to better reflect actual magic comment - // parsing from CRuby, but this at least gets us part of the way there. - if (encoding_start == NULL) return; - - // Skip any non-newline whitespace after the "coding:" or "coding=". - encoding_start += yp_strspn_inline_whitespace(encoding_start, end - encoding_start); - - // Now determine the end of the encoding string. This is either the end of - // the line, the first whitespace character, or a punctuation mark. - const char *encoding_end = yp_strpbrk(encoding_start, " \t\f\r\v\n;,", end - encoding_start); - encoding_end = encoding_end == NULL ? end : encoding_end; - - // Finally, we can determine the width of the encoding string. - size_t width = (size_t) (encoding_end - encoding_start); - - // First, we're going to call out to a user-defined callback if one was - // provided. If they return an encoding struct that we can use, then we'll - // use that here. - if (parser->encoding_decode_callback != NULL) { - yp_encoding_t *encoding = parser->encoding_decode_callback(parser, encoding_start, width); - - if (encoding != NULL) { - parser->encoding = *encoding; - return; - } - } - - // Next, we're going to loop through each of the encodings that we handle - // explicitly. If we found one that we understand, we'll use that value. -#define ENCODING(value, prebuilt) \ - if (width == sizeof(value) - 1 && strncasecmp(encoding_start, value, sizeof(value) - 1) == 0) { \ - parser->encoding = prebuilt; \ - if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser); \ - return; \ - } - - // Extensions like utf-8 can contain extra encoding details like, - // utf-8-dos, utf-8-linux, utf-8-mac - // We treat these all as utf-8 should treat any encoding starting utf-8 as utf-8 -#define ENCODING_EXTENDABLE(value, prebuilt) \ - if (strncasecmp(encoding_start, value, sizeof(value) - 1) == 0) { \ - parser->encoding = prebuilt; \ - if (parser->encoding_changed_callback != NULL) parser->encoding_changed_callback(parser); \ - return; \ - } - - // Check most common first. (This is pretty arbitrary.) - ENCODING_EXTENDABLE("utf-8", yp_encoding_utf_8); - ENCODING("ascii", yp_encoding_ascii); - ENCODING("ascii-8bit", yp_encoding_ascii_8bit); - ENCODING("us-ascii", yp_encoding_ascii); - ENCODING("binary", yp_encoding_ascii_8bit); - ENCODING("shift_jis", yp_encoding_shift_jis); - ENCODING("euc-jp", yp_encoding_euc_jp); - - // Then check all the others. - ENCODING("big5", yp_encoding_big5); - ENCODING("iso-8859-1", yp_encoding_iso_8859_1); - ENCODING("iso-8859-2", yp_encoding_iso_8859_2); - ENCODING("iso-8859-3", yp_encoding_iso_8859_3); - ENCODING("iso-8859-4", yp_encoding_iso_8859_4); - ENCODING("iso-8859-5", yp_encoding_iso_8859_5); - ENCODING("iso-8859-6", yp_encoding_iso_8859_6); - ENCODING("iso-8859-7", yp_encoding_iso_8859_7); - ENCODING("iso-8859-8", yp_encoding_iso_8859_8); - ENCODING("iso-8859-9", yp_encoding_iso_8859_9); - ENCODING("iso-8859-10", yp_encoding_iso_8859_10); - ENCODING("iso-8859-11", yp_encoding_iso_8859_11); - ENCODING("iso-8859-13", yp_encoding_iso_8859_13); - ENCODING("iso-8859-14", yp_encoding_iso_8859_14); - ENCODING("iso-8859-15", yp_encoding_iso_8859_15); - ENCODING("iso-8859-16", yp_encoding_iso_8859_16); - ENCODING("windows-31j", yp_encoding_windows_31j); - ENCODING("windows-1251", yp_encoding_windows_1251); - ENCODING("windows-1252", yp_encoding_windows_1252); - ENCODING("cp1251", yp_encoding_windows_1251); - ENCODING("cp1252", yp_encoding_windows_1252); - ENCODING("cp932", yp_encoding_windows_31j); - -#undef ENCODING - - // If nothing was returned by this point, then we've got an issue because we - // didn't understand the encoding that the user was trying to use. In this - // case we'll keep using the default encoding but add an error to the - // parser to indicate an unsuccessful parse. - yp_diagnostic_list_append(&parser->error_list, encoding_start, encoding_end, "Could not understand the encoding specified in the magic comment."); -} - -/******************************************************************************/ -/* Context manipulations */ -/******************************************************************************/ - -static bool -context_terminator(yp_context_t context, yp_token_t *token) { - switch (context) { - case YP_CONTEXT_MAIN: - case YP_CONTEXT_DEF_PARAMS: - return token->type == YP_TOKEN_EOF; - case YP_CONTEXT_DEFAULT_PARAMS: - return token->type == YP_TOKEN_COMMA || token->type == YP_TOKEN_PARENTHESIS_RIGHT; - case YP_CONTEXT_PREEXE: - case YP_CONTEXT_POSTEXE: - return token->type == YP_TOKEN_BRACE_RIGHT; - case YP_CONTEXT_MODULE: - case YP_CONTEXT_CLASS: - case YP_CONTEXT_SCLASS: - case YP_CONTEXT_LAMBDA_DO_END: - case YP_CONTEXT_DEF: - case YP_CONTEXT_BLOCK_KEYWORDS: - return token->type == YP_TOKEN_KEYWORD_END || token->type == YP_TOKEN_KEYWORD_RESCUE || token->type == YP_TOKEN_KEYWORD_ENSURE; - case YP_CONTEXT_WHILE: - case YP_CONTEXT_UNTIL: - case YP_CONTEXT_ELSE: - case YP_CONTEXT_FOR: - case YP_CONTEXT_ENSURE: - return token->type == YP_TOKEN_KEYWORD_END; - case YP_CONTEXT_CASE_WHEN: - return token->type == YP_TOKEN_KEYWORD_WHEN || token->type == YP_TOKEN_KEYWORD_END || token->type == YP_TOKEN_KEYWORD_ELSE; - case YP_CONTEXT_CASE_IN: - return token->type == YP_TOKEN_KEYWORD_IN || token->type == YP_TOKEN_KEYWORD_END || token->type == YP_TOKEN_KEYWORD_ELSE; - case YP_CONTEXT_IF: - case YP_CONTEXT_ELSIF: - return token->type == YP_TOKEN_KEYWORD_ELSE || token->type == YP_TOKEN_KEYWORD_ELSIF || token->type == YP_TOKEN_KEYWORD_END; - case YP_CONTEXT_UNLESS: - return token->type == YP_TOKEN_KEYWORD_ELSE || token->type == YP_TOKEN_KEYWORD_END; - case YP_CONTEXT_EMBEXPR: - return token->type == YP_TOKEN_EMBEXPR_END; - case YP_CONTEXT_BLOCK_BRACES: - return token->type == YP_TOKEN_BRACE_RIGHT; - case YP_CONTEXT_PARENS: - return token->type == YP_TOKEN_PARENTHESIS_RIGHT; - case YP_CONTEXT_BEGIN: - case YP_CONTEXT_RESCUE: - return token->type == YP_TOKEN_KEYWORD_ENSURE || token->type == YP_TOKEN_KEYWORD_RESCUE || token->type == YP_TOKEN_KEYWORD_ELSE || token->type == YP_TOKEN_KEYWORD_END; - case YP_CONTEXT_RESCUE_ELSE: - return token->type == YP_TOKEN_KEYWORD_ENSURE || token->type == YP_TOKEN_KEYWORD_END; - case YP_CONTEXT_LAMBDA_BRACES: - return token->type == YP_TOKEN_BRACE_RIGHT; - case YP_CONTEXT_PREDICATE: - return token->type == YP_TOKEN_KEYWORD_THEN || token->type == YP_TOKEN_NEWLINE || token->type == YP_TOKEN_SEMICOLON; - } - - return false; -} - -static bool -context_recoverable(yp_parser_t *parser, yp_token_t *token) { - yp_context_node_t *context_node = parser->current_context; - - while (context_node != NULL) { - if (context_terminator(context_node->context, token)) return true; - context_node = context_node->prev; - } - - return false; -} - -static void -context_push(yp_parser_t *parser, yp_context_t context) { - yp_context_node_t *context_node = (yp_context_node_t *) malloc(sizeof(yp_context_node_t)); - *context_node = (yp_context_node_t) { .context = context, .prev = NULL }; - - if (parser->current_context == NULL) { - parser->current_context = context_node; - } else { - context_node->prev = parser->current_context; - parser->current_context = context_node; - } -} - -static void -context_pop(yp_parser_t *parser) { - if (parser->current_context->prev == NULL) { - free(parser->current_context); - parser->current_context = NULL; - } else { - yp_context_node_t *prev = parser->current_context->prev; - free(parser->current_context); - parser->current_context = prev; - } -} - -static bool -context_p(yp_parser_t *parser, yp_context_t context) { - yp_context_node_t *context_node = parser->current_context; - - while (context_node != NULL) { - if (context_node->context == context) return true; - context_node = context_node->prev; - } - - return false; -} - -static bool -context_def_p(yp_parser_t *parser) { - yp_context_node_t *context_node = parser->current_context; - - while (context_node != NULL) { - switch (context_node->context) { - case YP_CONTEXT_DEF: - return true; - case YP_CONTEXT_CLASS: - case YP_CONTEXT_MODULE: - case YP_CONTEXT_SCLASS: - return false; - default: - context_node = context_node->prev; - } - } - - return false; -} - -/******************************************************************************/ -/* Specific token lexers */ -/******************************************************************************/ - -static yp_token_type_t -lex_optional_float_suffix(yp_parser_t *parser) { - yp_token_type_t type = YP_TOKEN_INTEGER; - - // Here we're going to attempt to parse the optional decimal portion of a - // float. If it's not there, then it's okay and we'll just continue on. - if (peek(parser) == '.') { - if (yp_char_is_decimal_digit(peek_at(parser, 1))) { - parser->current.end += 2; - parser->current.end += yp_strspn_decimal_number(parser->current.end, parser->end - parser->current.end); - type = YP_TOKEN_FLOAT; - } else { - // If we had a . and then something else, then it's not a float suffix on - // a number it's a method call or something else. - return type; - } - } - - // Here we're going to attempt to parse the optional exponent portion of a - // float. If it's not there, it's okay and we'll just continue on. - if (match(parser, 'e') || match(parser, 'E')) { - (void) (match(parser, '+') || match(parser, '-')); - - if (yp_char_is_decimal_digit(*parser->current.end)) { - parser->current.end++; - parser->current.end += yp_strspn_decimal_number(parser->current.end, parser->end - parser->current.end); - type = YP_TOKEN_FLOAT; - } else { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Missing exponent."); - type = YP_TOKEN_FLOAT; - } - } - - return type; -} - -static yp_token_type_t -lex_numeric_prefix(yp_parser_t *parser) { - yp_token_type_t type = YP_TOKEN_INTEGER; - - if (parser->current.end[-1] == '0') { - switch (*parser->current.end) { - // 0d1111 is a decimal number - case 'd': - case 'D': - if (yp_char_is_decimal_digit(*++parser->current.end)) { - parser->current.end += yp_strspn_decimal_number(parser->current.end, parser->end - parser->current.end); - } else { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid decimal number."); - } - - break; - - // 0b1111 is a binary number - case 'b': - case 'B': - if (yp_char_is_binary_digit(*++parser->current.end)) { - parser->current.end += yp_strspn_binary_number(parser->current.end, parser->end - parser->current.end); - } else { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid binary number."); - } - - break; - - // 0o1111 is an octal number - case 'o': - case 'O': - if (yp_char_is_octal_digit(*++parser->current.end)) { - parser->current.end += yp_strspn_octal_number(parser->current.end, parser->end - parser->current.end); - } else { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid octal number."); - } - - break; - - // 01111 is an octal number - case '_': - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - parser->current.end += yp_strspn_octal_number(parser->current.end, parser->end - parser->current.end); - break; - - // 0x1111 is a hexadecimal number - case 'x': - case 'X': - if (yp_char_is_hexadecimal_digit(*++parser->current.end)) { - parser->current.end += yp_strspn_hexadecimal_number(parser->current.end, parser->end - parser->current.end); - } else { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid hexadecimal number."); - } - - break; - - // 0.xxx is a float - case '.': { - type = lex_optional_float_suffix(parser); - break; - } - - // 0exxx is a float - case 'e': - case 'E': { - type = lex_optional_float_suffix(parser); - break; - } - } - } else { - // If it didn't start with a 0, then we'll lex as far as we can into a - // decimal number. - parser->current.end += yp_strspn_decimal_number(parser->current.end, parser->end - parser->current.end); - - // Afterward, we'll lex as far as we can into an optional float suffix. - type = lex_optional_float_suffix(parser); - } - - // If the last character that we consumed was an underscore, then this is - // actually an invalid integer value, and we should return an invalid token. - if (parser->current.end[-1] == '_') { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Number literal cannot end with a `_`."); - } - - return type; -} - -static yp_token_type_t -lex_finalize_numeric_type(yp_parser_t *parser, yp_token_type_t numeric_type, const char *numeric_end, const char *rational_end, const char *imaginary_end) { - if (rational_end || imaginary_end) { - lex_mode_push(parser, (yp_lex_mode_t) { - .mode = YP_LEX_NUMERIC, - .as.numeric.type = numeric_type, - .as.numeric.start = parser->current.start, - .as.numeric.end = numeric_end - }); - } - - if (rational_end && imaginary_end) { - lex_mode_push(parser, (yp_lex_mode_t) { - .mode = YP_LEX_NUMERIC, - .as.numeric.type = YP_TOKEN_RATIONAL_NUMBER, - .as.numeric.start = parser->current.start, - .as.numeric.end = rational_end - }); - } - - if (imaginary_end) { - return YP_TOKEN_IMAGINARY_NUMBER; - } - - if (rational_end) { - return YP_TOKEN_RATIONAL_NUMBER; - } - - return numeric_type; -} - -static yp_token_type_t -lex_numeric(yp_parser_t *parser) { - yp_token_type_t type = YP_TOKEN_INTEGER; - - if (parser->current.end < parser->end) { - type = lex_numeric_prefix(parser); - - const char *end = parser->current.end; - const char *rational_end = NULL; - const char *imaginary_end = NULL; - - if (match(parser, 'r')) { - rational_end = parser->current.end; - } - - if (match(parser, 'i')) { - imaginary_end = parser->current.end; - } - - const unsigned char uc = (const unsigned char) peek(parser); - if (uc != '\0' && (uc >= 0x80 || ((uc >= 'a' && uc <= 'z') || (uc >= 'A' && uc <= 'Z')) || uc == '_')) { - parser->current.end = end; - } else { - type = lex_finalize_numeric_type(parser, type, end, rational_end, imaginary_end); - } - } - - return type; -} - -static yp_token_type_t -lex_global_variable(yp_parser_t *parser) { - switch (*parser->current.end) { - case '~': // $~: match-data - case '*': // $*: argv - case '$': // $$: pid - case '?': // $?: last status - case '!': // $!: error string - case '@': // $@: error position - case '/': // $/: input record separator - case '\\': // $\: output record separator - case ';': // $;: field separator - case ',': // $,: output field separator - case '.': // $.: last read line number - case '=': // $=: ignorecase - case ':': // $:: load path - case '<': // $<: reading filename - case '>': // $>: default output handle - case '\"': // $": already loaded files - parser->current.end++; - return YP_TOKEN_GLOBAL_VARIABLE; - - case '&': // $&: last match - case '`': // $`: string before last match - case '\'': // $': string after last match - case '+': // $+: string matches last paren. - parser->current.end++; - return lex_state_p(parser, YP_LEX_STATE_FNAME) ? YP_TOKEN_GLOBAL_VARIABLE : YP_TOKEN_BACK_REFERENCE; - - case '0': { - parser->current.end++; - size_t width; - - if ((width = char_is_identifier(parser, parser->current.end)) > 0) { - do { - parser->current.end += width; - } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0); - - // $0 isn't allowed to be followed by anything. - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid global variable."); - } - - return YP_TOKEN_GLOBAL_VARIABLE; - } - - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': - parser->current.end += yp_strspn_decimal_digit(parser->current.end, parser->end - parser->current.end); - return lex_state_p(parser, YP_LEX_STATE_FNAME) ? YP_TOKEN_GLOBAL_VARIABLE : YP_TOKEN_NTH_REFERENCE; - - case '-': - parser->current.end++; - /* fallthrough */ - default: { - size_t width; - - if ((width = char_is_identifier(parser, parser->current.end)) > 0) { - do { - parser->current.end += width; - } while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0); - } else { - // If we get here, then we have a $ followed by something that isn't - // recognized as a global variable. - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid global variable."); - } - - return YP_TOKEN_GLOBAL_VARIABLE; - } - } -} - -// This function checks if the current token matches a keyword. If it does, it -// returns true. Otherwise, it returns false. The arguments are as follows: -// -// * `value` - the literal string that we're checking for -// * `width` - the length of the token -// * `state` - the state that we should transition to if the token matches -// -static yp_token_type_t -lex_keyword(yp_parser_t *parser, const char *value, yp_lex_state_t state, yp_token_type_t type, yp_token_type_t modifier_type) { - yp_lex_state_t last_state = parser->lex_state; - - if (strncmp(parser->current.start, value, strlen(value)) == 0) { - if (parser->lex_state & YP_LEX_STATE_FNAME) { - lex_state_set(parser, YP_LEX_STATE_ENDFN); - } else { - lex_state_set(parser, state); - if (state == YP_LEX_STATE_BEG) { - parser->command_start = true; - } - - if ((modifier_type != YP_TOKEN_EOF) && !(last_state & (YP_LEX_STATE_BEG | YP_LEX_STATE_LABELED | YP_LEX_STATE_CLASS))) { - lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); - return modifier_type; - } - } - - return type; - } - - return YP_TOKEN_EOF; -} - -static yp_token_type_t -lex_identifier(yp_parser_t *parser, bool previous_command_start) { - // Lex as far as we can into the current identifier. - size_t width; - while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0) { - parser->current.end += width; - } - - // Now cache the length of the identifier so that we can quickly compare it - // against known keywords. - width = (size_t) (parser->current.end - parser->current.start); - - if (parser->current.end < parser->end) { - if (((parser->current.end + 1 >= parser->end) || (parser->current.end[1] != '=')) && (match(parser, '!') || match(parser, '?'))) { - // First we'll attempt to extend the identifier by a ! or ?. Then we'll - // check if we're returning the defined? keyword or just an identifier. - width++; - - if ( - ((lex_state_p(parser, YP_LEX_STATE_LABEL | YP_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) && - (peek(parser) == ':') && (peek_at(parser, 1) != ':') - ) { - // If we're in a position where we can accept a : at the end of an - // identifier, then we'll optionally accept it. - lex_state_set(parser, YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED); - (void) match(parser, ':'); - return YP_TOKEN_LABEL; - } - - if (parser->lex_state != YP_LEX_STATE_DOT) { - if (width == 8 && (lex_keyword(parser, "defined?", YP_LEX_STATE_ARG, YP_TOKEN_KEYWORD_DEFINED, YP_TOKEN_EOF) != YP_TOKEN_EOF)) { - return YP_TOKEN_KEYWORD_DEFINED; - } - } - - return YP_TOKEN_IDENTIFIER; - } else if (lex_state_p(parser, YP_LEX_STATE_FNAME) && peek_at(parser, 1) != '~' && peek_at(parser, 1) != '>' && (peek_at(parser, 1) != '=' || peek_at(parser, 2) == '>') && match(parser, '=')) { - // If we're in a position where we can accept a = at the end of an - // identifier, then we'll optionally accept it. - return YP_TOKEN_IDENTIFIER; - } - - if ( - ((lex_state_p(parser, YP_LEX_STATE_LABEL | YP_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser)) && - peek(parser) == ':' && peek_at(parser, 1) != ':' - ) { - // If we're in a position where we can accept a : at the end of an - // identifier, then we'll optionally accept it. - lex_state_set(parser, YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED); - (void) match(parser, ':'); - return YP_TOKEN_LABEL; - } - } - - if (parser->lex_state != YP_LEX_STATE_DOT) { - yp_token_type_t type; - - switch (width) { - case 2: - if (lex_keyword(parser, "do", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_DO, YP_TOKEN_EOF) != YP_TOKEN_EOF) { - if (yp_do_loop_stack_p(parser)) { - return YP_TOKEN_KEYWORD_DO_LOOP; - } - return YP_TOKEN_KEYWORD_DO; - } - - if ((type = lex_keyword(parser, "if", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_IF, YP_TOKEN_KEYWORD_IF_MODIFIER)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "in", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_IN, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "or", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_OR, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - break; - case 3: - if ((type = lex_keyword(parser, "and", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_AND, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "def", YP_LEX_STATE_FNAME, YP_TOKEN_KEYWORD_DEF, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "end", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_END, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "END", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_END_UPCASE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "for", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_FOR, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "nil", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_NIL, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "not", YP_LEX_STATE_ARG, YP_TOKEN_KEYWORD_NOT, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - break; - case 4: - if ((type = lex_keyword(parser, "case", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_CASE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "else", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "next", YP_LEX_STATE_MID, YP_TOKEN_KEYWORD_NEXT, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "redo", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_REDO, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "self", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_SELF, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "then", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_THEN, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "true", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_TRUE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "when", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_WHEN, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - break; - case 5: - if ((type = lex_keyword(parser, "alias", YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM, YP_TOKEN_KEYWORD_ALIAS, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "begin", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_BEGIN, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "BEGIN", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_BEGIN_UPCASE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "break", YP_LEX_STATE_MID, YP_TOKEN_KEYWORD_BREAK, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "class", YP_LEX_STATE_CLASS, YP_TOKEN_KEYWORD_CLASS, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "elsif", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_ELSIF, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "false", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_FALSE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "retry", YP_LEX_STATE_END, YP_TOKEN_KEYWORD_RETRY, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "super", YP_LEX_STATE_ARG, YP_TOKEN_KEYWORD_SUPER, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "undef", YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM, YP_TOKEN_KEYWORD_UNDEF, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "until", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_UNTIL, YP_TOKEN_KEYWORD_UNTIL_MODIFIER)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "while", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_WHILE, YP_TOKEN_KEYWORD_WHILE_MODIFIER)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "yield", YP_LEX_STATE_ARG, YP_TOKEN_KEYWORD_YIELD, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - break; - case 6: - if ((type = lex_keyword(parser, "ensure", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "module", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_MODULE, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "rescue", YP_LEX_STATE_MID, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_RESCUE_MODIFIER)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "return", YP_LEX_STATE_MID, YP_TOKEN_KEYWORD_RETURN, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "unless", YP_LEX_STATE_BEG, YP_TOKEN_KEYWORD_UNLESS, YP_TOKEN_KEYWORD_UNLESS_MODIFIER)) != YP_TOKEN_EOF) return type; - break; - case 8: - if ((type = lex_keyword(parser, "__LINE__", YP_LEX_STATE_END, YP_TOKEN_KEYWORD___LINE__, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - if ((type = lex_keyword(parser, "__FILE__", YP_LEX_STATE_END, YP_TOKEN_KEYWORD___FILE__, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - break; - case 12: - if ((type = lex_keyword(parser, "__ENCODING__", YP_LEX_STATE_END, YP_TOKEN_KEYWORD___ENCODING__, YP_TOKEN_EOF)) != YP_TOKEN_EOF) return type; - break; - } - } - - return parser->encoding.isupper_char(parser->current.start) ? YP_TOKEN_CONSTANT : YP_TOKEN_IDENTIFIER; -} - -// Returns true if the current token that the parser is considering is at the -// beginning of a line or the beginning of the source. -static bool -current_token_starts_line(yp_parser_t *parser) { - return (parser->current.start == parser->start) || (parser->current.start[-1] == '\n'); -} - -// When we hit a # while lexing something like a string, we need to potentially -// handle interpolation. This function performs that check. It returns a token -// type representing what it found. Those cases are: -// -// * YP_TOKEN_NOT_PROVIDED - No interpolation was found at this point. The -// caller should keep lexing. -// * YP_TOKEN_STRING_CONTENT - No interpolation was found at this point. The -// caller should return this token type. -// * YP_TOKEN_EMBEXPR_BEGIN - An embedded expression was found. The caller -// should return this token type. -// * YP_TOKEN_EMBVAR - An embedded variable was found. The caller should return -// this token type. -// -static yp_token_type_t -lex_interpolation(yp_parser_t *parser, const char *pound) { - // If there is no content following this #, then we're at the end of - // the string and we can safely return string content. - if (pound + 1 >= parser->end) { - parser->current.end = pound; - return YP_TOKEN_STRING_CONTENT; - } - - // Now we'll check against the character the follows the #. If it constitutes - // valid interplation, we'll handle that, otherwise we'll return - // YP_TOKEN_NOT_PROVIDED. - switch (pound[1]) { - case '@': { - // In this case we may have hit an embedded instance or class variable. - if (pound + 2 >= parser->end) { - parser->current.end = pound + 1; - return YP_TOKEN_STRING_CONTENT; - } - - // If we're looking at a @ and there's another @, then we'll skip past the - // second @. - const char *variable = pound + 2; - if (*variable == '@' && pound + 3 < parser->end) variable++; - - if (char_is_identifier_start(parser, variable)) { - // At this point we're sure that we've either hit an embedded instance - // or class variable. In this case we'll first need to check if we've - // already consumed content. - if (pound > parser->current.start) { - parser->current.end = pound; - return YP_TOKEN_STRING_CONTENT; - } - - // Otherwise we need to return the embedded variable token - // and then switch to the embedded variable lex mode. - lex_mode_push(parser, (yp_lex_mode_t) { .mode = YP_LEX_EMBVAR }); - parser->current.end = pound + 1; - return YP_TOKEN_EMBVAR; - } - - // If we didn't get an valid interpolation, then this is just regular - // string content. This is like if we get "#@-". In this case the caller - // should keep lexing. - parser->current.end = variable; - return YP_TOKEN_NOT_PROVIDED; - } - case '$': - // In this case we may have hit an embedded global variable. If there's - // not enough room, then we'll just return string content. - if (pound + 2 >= parser->end) { - parser->current.end = pound + 1; - return YP_TOKEN_STRING_CONTENT; - } - - // This is the character that we're going to check to see if it is the - // start of an identifier that would indicate that this is a global - // variable. - const char *check = pound + 2; - - if (pound[2] == '-') { - if (pound + 3 >= parser->end) { - parser->current.end = pound + 2; - return YP_TOKEN_STRING_CONTENT; - } - - check++; - } - - // If the character that we're going to check is the start of an - // identifier, or we don't have a - and the character is a decimal number - // or a global name punctuation character, then we've hit an embedded - // global variable. - if ( - char_is_identifier_start(parser, check) || - (pound[2] != '-' && (yp_char_is_decimal_digit(pound[2]) || char_is_global_name_punctuation(pound[2]))) - ) { - // In this case we've hit an embedded global variable. First check to - // see if we've already consumed content. If we have, then we need to - // return that content as string content first. - if (pound > parser->current.start) { - parser->current.end = pound; - return YP_TOKEN_STRING_CONTENT; - } - - // Otherwise, we need to return the embedded variable token and switch - // to the embedded variable lex mode. - lex_mode_push(parser, (yp_lex_mode_t) { .mode = YP_LEX_EMBVAR }); - parser->current.end = pound + 1; - return YP_TOKEN_EMBVAR; - } - - // In this case we've hit a #$ that does not indicate a global variable. - // In this case we'll continue lexing past it. - parser->current.end = pound + 1; - return YP_TOKEN_NOT_PROVIDED; - case '{': - // In this case it's the start of an embedded expression. If we have - // already consumed content, then we need to return that content as string - // content first. - if (pound > parser->current.start) { - parser->current.end = pound; - return YP_TOKEN_STRING_CONTENT; - } - - parser->enclosure_nesting++; - - // Otherwise we'll skip past the #{ and begin lexing the embedded - // expression. - lex_mode_push(parser, (yp_lex_mode_t) { .mode = YP_LEX_EMBEXPR }); - parser->current.end = pound + 2; - parser->command_start = true; - yp_do_loop_stack_push(parser, false); - return YP_TOKEN_EMBEXPR_BEGIN; - default: - // In this case we've hit a # that doesn't constitute interpolation. We'll - // mark that by returning the not provided token type. This tells the - // consumer to keep lexing forward. - parser->current.end = pound + 1; - return YP_TOKEN_NOT_PROVIDED; - } -} - -// This function is responsible for lexing either a character literal or the ? -// operator. The supported character literals are described below. -// -// \a bell, ASCII 07h (BEL) -// \b backspace, ASCII 08h (BS) -// \t horizontal tab, ASCII 09h (TAB) -// \n newline (line feed), ASCII 0Ah (LF) -// \v vertical tab, ASCII 0Bh (VT) -// \f form feed, ASCII 0Ch (FF) -// \r carriage return, ASCII 0Dh (CR) -// \e escape, ASCII 1Bh (ESC) -// \s space, ASCII 20h (SPC) -// \\ backslash -// \nnn octal bit pattern, where nnn is 1-3 octal digits ([0-7]) -// \xnn hexadecimal bit pattern, where nn is 1-2 hexadecimal digits ([0-9a-fA-F]) -// \unnnn Unicode character, where nnnn is exactly 4 hexadecimal digits ([0-9a-fA-F]) -// \u{nnnn ...} Unicode character(s), where each nnnn is 1-6 hexadecimal digits ([0-9a-fA-F]) -// \cx or \C-x control character, where x is an ASCII printable character -// \M-x meta character, where x is an ASCII printable character -// \M-\C-x meta control character, where x is an ASCII printable character -// \M-\cx same as above -// \c\M-x same as above -// \c? or \C-? delete, ASCII 7Fh (DEL) -// -static yp_token_type_t -lex_question_mark(yp_parser_t *parser) { - if (lex_state_end_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_BEG); - return YP_TOKEN_QUESTION_MARK; - } - - if (parser->current.end >= parser->end) { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Incomplete character syntax."); - return YP_TOKEN_CHARACTER_LITERAL; - } - - if (yp_char_is_whitespace(*parser->current.end)) { - lex_state_set(parser, YP_LEX_STATE_BEG); - return YP_TOKEN_QUESTION_MARK; - } - - lex_state_set(parser, YP_LEX_STATE_BEG); - - if (parser->current.start[1] == '\\') { - lex_state_set(parser, YP_LEX_STATE_END); - parser->current.end += yp_unescape_calculate_difference(parser->current.start + 1, parser->end, YP_UNESCAPE_ALL, true, &parser->error_list); - return YP_TOKEN_CHARACTER_LITERAL; - } else { - size_t encoding_width = parser->encoding.char_width(parser->current.end); - // We only want to return a character literal if there's exactly one - // alphanumeric character right after the `?` - if ( - !parser->encoding.alnum_char(parser->current.end) || - !parser->encoding.alnum_char(parser->current.end + encoding_width) - ) { - lex_state_set(parser, YP_LEX_STATE_END); - parser->current.end += encoding_width; - return YP_TOKEN_CHARACTER_LITERAL; - } - } - - return YP_TOKEN_QUESTION_MARK; -} - -// Lex a variable that starts with an @ sign (either an instance or class -// variable). -static yp_token_type_t -lex_at_variable(yp_parser_t *parser) { - yp_token_type_t type = match(parser, '@') ? YP_TOKEN_CLASS_VARIABLE : YP_TOKEN_INSTANCE_VARIABLE; - size_t width; - - if (parser->current.end < parser->end && (width = char_is_identifier_start(parser, parser->current.end)) > 0) { - parser->current.end += width; - - while (parser->current.end < parser->end && (width = char_is_identifier(parser, parser->current.end)) > 0) { - parser->current.end += width; - } - } else if (type == YP_TOKEN_CLASS_VARIABLE) { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Incomplete class variable."); - } else { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Incomplete instance variable."); - } - - // If we're lexing an embedded variable, then we need to pop back into the - // parent lex context. - if (parser->lex_modes.current->mode == YP_LEX_EMBVAR) { - lex_mode_pop(parser); - } - - return type; -} - -// Optionally call out to the lex callback if one is provided. -static inline void -parser_lex_callback(yp_parser_t *parser) { - if (parser->lex_callback) { - parser->lex_callback->callback(parser->lex_callback->data, parser, &parser->current); - } -} - -// Return a new comment node of the specified type. -static inline yp_comment_t * -parser_comment(yp_parser_t *parser, yp_comment_type_t type) { - yp_comment_t *comment = (yp_comment_t *) malloc(sizeof(yp_comment_t)); - *comment = (yp_comment_t) { - .type = type, - .start = parser->current.start, - .end = parser->current.end - }; - - return comment; -} - -// Lex out embedded documentation, and return when we have either hit the end of -// the file or the end of the embedded documentation. This calls the callback -// manually because only the lexer should see these tokens, not the parser. -static yp_token_type_t -lex_embdoc(yp_parser_t *parser) { - // First, lex out the EMBDOC_BEGIN token. - const char *newline = memchr(parser->current.end, '\n', (size_t) (parser->end - parser->current.end)); - parser->current.end = newline == NULL ? parser->end : newline + 1; - parser->current.type = YP_TOKEN_EMBDOC_BEGIN; - parser_lex_callback(parser); - - // Now, create a comment that is going to be attached to the parser. - yp_comment_t *comment = parser_comment(parser, YP_COMMENT_EMBDOC); - - // Now, loop until we find the end of the embedded documentation or the end of - // the file. - while (parser->current.end + 4 <= parser->end) { - parser->current.start = parser->current.end; - - // If we've hit the end of the embedded documentation then we'll return that - // token here. - if (strncmp(parser->current.end, "=end", 4) == 0 && - (parser->current.end + 4 == parser->end || yp_char_is_whitespace(parser->current.end[4]))) { - const char *newline = memchr(parser->current.end, '\n', (size_t) (parser->end - parser->current.end)); - parser->current.end = newline == NULL ? parser->end : newline + 1; - parser->current.type = YP_TOKEN_EMBDOC_END; - parser_lex_callback(parser); - - comment->end = parser->current.end; - yp_list_append(&parser->comment_list, (yp_list_node_t *) comment); - - return YP_TOKEN_EMBDOC_END; - } - - // Otherwise, we'll parse until the end of the line and return a line of - // embedded documentation. - const char *newline = memchr(parser->current.end, '\n', (size_t) (parser->end - parser->current.end)); - parser->current.end = newline == NULL ? parser->end : newline + 1; - parser->current.type = YP_TOKEN_EMBDOC_LINE; - parser_lex_callback(parser); - } - - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unterminated embdoc"); - - comment->end = parser->current.end; - yp_list_append(&parser->comment_list, (yp_list_node_t *) comment); - - return YP_TOKEN_EOF; -} - -// Set the current type to an ignored newline and then call the lex callback. -// This happens in a couple places depending on whether or not we have already -// lexed a comment. -static inline void -parser_lex_ignored_newline(yp_parser_t *parser) { - parser->current.type = YP_TOKEN_IGNORED_NEWLINE; - parser_lex_callback(parser); -} - -// This function will be called when a newline is encountered. In some newlines, -// we need to check if there is a heredoc or heredocs that we have already lexed -// the body of that we need to now skip past. That will be indicated by the -// heredoc_end field on the parser. -// -// If it is set, then we need to skip past the heredoc body and then clear the -// heredoc_end field. -static inline void -parser_flush_heredoc_end(yp_parser_t *parser) { - assert(parser->heredoc_end <= parser->end); - parser->next_start = parser->heredoc_end; - parser->heredoc_end = NULL; -} - -// This is a convenience macro that will set the current token type, call the -// lex callback, and then return from the parser_lex function. -#define LEX(token_type) parser->current.type = token_type; parser_lex_callback(parser); return - -// Called when the parser requires a new token. The parser maintains a moving -// window of two tokens at a time: parser.previous and parser.current. This -// function will move the current token into the previous token and then -// lex a new token into the current token. -static void -parser_lex(yp_parser_t *parser) { - assert(parser->current.end <= parser->end); - parser->previous = parser->current; - - // This value mirrors cmd_state from CRuby. - bool previous_command_start = parser->command_start; - parser->command_start = false; - - // This is used to communicate to the newline lexing function that we've - // already seen a comment. - bool lexed_comment = false; - - switch (parser->lex_modes.current->mode) { - case YP_LEX_DEFAULT: - case YP_LEX_EMBEXPR: - case YP_LEX_EMBVAR: - case YP_LEX_NUMERIC: - - // We have a specific named label here because we are going to jump back to - // this location in the event that we have lexed a token that should not be - // returned to the parser. This includes comments, ignored newlines, and - // invalid tokens of some form. - lex_next_token: { - // If we have the special next_start pointer set, then we're going to jump - // to that location and start lexing from there. - if (parser->next_start != NULL) { - parser->current.end = parser->next_start; - parser->next_start = NULL; - } - - // This value mirrors space_seen from CRuby. It tracks whether or not - // space has been eaten before the start of the next token. - bool space_seen = false; - - // First, we're going to skip past any whitespace at the front of the next - // token. - bool chomping = true; - while (parser->current.end < parser->end && chomping) { - switch (*parser->current.end) { - case ' ': - case '\t': - case '\f': - case '\v': - parser->current.end++; - space_seen = true; - break; - case '\r': - if (peek_at(parser, 1) == '\n') { - chomping = false; - } else { - parser->current.end++; - space_seen = true; - } - break; - case '\\': - if (peek_at(parser, 1) == '\n') { - parser->current.end += 2; - space_seen = true; - } else if (parser->current.end + 2 < parser->end && peek_at(parser, 1) == '\r' && peek_at(parser, 2) == '\n') { - parser->current.end += 3; - space_seen = true; - } else if (yp_char_is_inline_whitespace(*parser->current.end)) { - parser->current.end += 2; - } else { - chomping = false; - } - break; - default: - chomping = false; - break; - } - } - - // Next, we'll set to start of this token to be the current end. - parser->current.start = parser->current.end; - - // We'll check if we're at the end of the file. If we are, then we need to - // return the EOF token. - if (parser->current.end >= parser->end) { - LEX(YP_TOKEN_EOF); - } - - // Finally, we'll check the current character to determine the next token. - switch (*parser->current.end++) { - case '\0': // NUL or end of script - case '\004': // ^D - case '\032': // ^Z - parser->current.end--; - LEX(YP_TOKEN_EOF); - - case '#': { // comments - const char *ending = memchr(parser->current.end, '\n', (size_t) (parser->end - parser->current.end)); - while (ending && ending < parser->end && *ending != '\n') { - ending = memchr(ending + 1, '\n', (size_t) (parser->end - ending)); - } - - parser->current.end = ending == NULL ? parser->end : ending + 1; - parser->current.type = YP_TOKEN_COMMENT; - parser_lex_callback(parser); - - // If we found a comment while lexing, then we're going to add it to the - // list of comments in the file and keep lexing. - yp_comment_t *comment = parser_comment(parser, YP_COMMENT_INLINE); - yp_list_append(&parser->comment_list, (yp_list_node_t *) comment); - - if (parser->current.start == parser->encoding_comment_start) { - parser_lex_encoding_comment(parser); - } - - lexed_comment = true; - } - /* fallthrough */ - case '\r': { - // The only way you can have carriage returns in this particular loop - // is if you have a carriage return followed by a newline. In that - // case we'll just skip over the carriage return and continue lexing, - // in order to make it so that the newline token encapsulates both the - // carriage return and the newline. Note that we need to check that - // we haven't already lexed a comment here because that falls through - // into here as well. - if (!lexed_comment) parser->current.end++; - } - /* fallthrough */ - case '\n': { - if (parser->heredoc_end != NULL) { - parser_flush_heredoc_end(parser); - } - - // If this is an ignored newline, then we can continue lexing after - // calling the callback with the ignored newline token. - switch (lex_state_ignored_p(parser)) { - case YP_IGNORED_NEWLINE_NONE: - break; - case YP_IGNORED_NEWLINE_PATTERN: - if (parser->pattern_matching_newlines || parser->in_keyword_arg) { - if (!lexed_comment) parser_lex_ignored_newline(parser); - lex_state_set(parser, YP_LEX_STATE_BEG); - parser->command_start = true; - parser->current.type = YP_TOKEN_NEWLINE; - return; - } - /* fallthrough */ - case YP_IGNORED_NEWLINE_ALL: - if (!lexed_comment) parser_lex_ignored_newline(parser); - lexed_comment = false; - goto lex_next_token; - } - - // Here we need to look ahead and see if there is a call operator - // (either . or &.) that starts the next line. If there is, then this - // is going to become an ignored newline and we're going to instead - // return the call operator. - const char *next_content = parser->next_start == NULL ? parser->current.end : parser->next_start; - next_content += yp_strspn_inline_whitespace(next_content, parser->end - next_content); - - if (next_content < parser->end) { - // If we hit a comment after a newline, then we're going to check - // if it's ignored or if it's followed by a method call ('.'). - // If it is, then we're going to call the - // callback with an ignored newline and then continue lexing. - // Otherwise we'll return a regular newline. - if (next_content[0] == '#') { - // Here we look for a "." or "&." following a "\n". - const char *following = memchr(next_content, '\n', (size_t) (parser->end - next_content)); - - while (following && (following < parser->end)) { - following++; - following += yp_strspn_inline_whitespace(following, parser->end - following); - - // If this is not followed by a comment, then we can break out - // of this loop. - if (*following != '#') break; - - // If there is a comment, then we need to find the end of the - // comment and continue searching from there. - following = memchr(following, '\n', (size_t) (parser->end - following)); - } - - // If the lex state was ignored, or we hit a '.' or a '&.', - // we will lex the ignored newline - if (lex_state_ignored_p(parser) || (following && ((following[0] == '.') || (following + 1 < parser->end && following[0] == '&' && following[1] == '.')))) { - if (!lexed_comment) parser_lex_ignored_newline(parser); - lexed_comment = false; - goto lex_next_token; - } - } - - // If we hit a . after a newline, then we're in a call chain and - // we need to return the call operator. - if (next_content[0] == '.') { - // To match ripper, we need to emit an ignored newline even though - // its a real newline in the case that we have a beginless range - // on a subsequent line. - if ((next_content + 1 < parser->end) && (next_content[1] == '.')) { - if (!lexed_comment) parser_lex_ignored_newline(parser); - lex_state_set(parser, YP_LEX_STATE_BEG); - parser->command_start = true; - parser->current.type = YP_TOKEN_NEWLINE; - return; - } - - if (!lexed_comment) parser_lex_ignored_newline(parser); - lex_state_set(parser, YP_LEX_STATE_DOT); - parser->current.start = next_content; - parser->current.end = next_content + 1; - parser->next_start = NULL; - LEX(YP_TOKEN_DOT); - } - - // If we hit a &. after a newline, then we're in a call chain and - // we need to return the call operator. - if (next_content + 1 < parser->end && next_content[0] == '&' && next_content[1] == '.') { - if (!lexed_comment) parser_lex_ignored_newline(parser); - lex_state_set(parser, YP_LEX_STATE_DOT); - parser->current.start = next_content; - parser->current.end = next_content + 2; - parser->next_start = NULL; - LEX(YP_TOKEN_AMPERSAND_DOT); - } - } - - // At this point we know this is a regular newline, and we can set the - // necessary state and return the token. - lex_state_set(parser, YP_LEX_STATE_BEG); - parser->command_start = true; - parser->current.type = YP_TOKEN_NEWLINE; - if (!lexed_comment) parser_lex_callback(parser); - return; - } - - // , - case ',': - lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); - LEX(YP_TOKEN_COMMA); - - // ( - case '(': { - yp_token_type_t type = YP_TOKEN_PARENTHESIS_LEFT; - - if (space_seen && (lex_state_arg_p(parser) || parser->lex_state == (YP_LEX_STATE_END | YP_LEX_STATE_LABEL))) { - type = YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES; - } - - parser->enclosure_nesting++; - lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); - yp_do_loop_stack_push(parser, false); - LEX(type); - } - - // ) - case ')': - parser->enclosure_nesting--; - lex_state_set(parser, YP_LEX_STATE_ENDFN); - yp_do_loop_stack_pop(parser); - LEX(YP_TOKEN_PARENTHESIS_RIGHT); - - // ; - case ';': - lex_state_set(parser, YP_LEX_STATE_BEG); - parser->command_start = true; - LEX(YP_TOKEN_SEMICOLON); - - // [ [] []= - case '[': - parser->enclosure_nesting++; - yp_token_type_t type = YP_TOKEN_BRACKET_LEFT; - - if (lex_state_operator_p(parser)) { - if (match(parser, ']')) { - parser->enclosure_nesting--; - lex_state_set(parser, YP_LEX_STATE_ARG); - LEX(match(parser, '=') ? YP_TOKEN_BRACKET_LEFT_RIGHT_EQUAL : YP_TOKEN_BRACKET_LEFT_RIGHT); - } - - lex_state_set(parser, YP_LEX_STATE_ARG | YP_LEX_STATE_LABEL); - LEX(type); - } - - if (lex_state_beg_p(parser) || (lex_state_arg_p(parser) && (space_seen || lex_state_p(parser, YP_LEX_STATE_LABELED)))) { - type = YP_TOKEN_BRACKET_LEFT_ARRAY; - } - - lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); - yp_do_loop_stack_push(parser, false); - LEX(type); - - // ] - case ']': - parser->enclosure_nesting--; - lex_state_set(parser, YP_LEX_STATE_END); - yp_do_loop_stack_pop(parser); - LEX(YP_TOKEN_BRACKET_RIGHT); - - // { - case '{': { - yp_token_type_t type = YP_TOKEN_BRACE_LEFT; - - if (parser->enclosure_nesting == parser->lambda_enclosure_nesting) { - // This { begins a lambda - parser->command_start = true; - lex_state_set(parser, YP_LEX_STATE_BEG); - type = YP_TOKEN_LAMBDA_BEGIN; - } else if (lex_state_p(parser, YP_LEX_STATE_LABELED)) { - // This { begins a hash literal - lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); - } else if (lex_state_p(parser, YP_LEX_STATE_ARG_ANY | YP_LEX_STATE_END | YP_LEX_STATE_ENDFN)) { - // This { begins a block - parser->command_start = true; - lex_state_set(parser, YP_LEX_STATE_BEG); - } else if (lex_state_p(parser, YP_LEX_STATE_ENDARG)) { - // This { begins a block on a command - parser->command_start = true; - lex_state_set(parser, YP_LEX_STATE_BEG); - } else { - // This { begins a hash literal - lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); - } - - parser->enclosure_nesting++; - parser->brace_nesting++; - yp_do_loop_stack_push(parser, false); - - LEX(type); - } - - // } - case '}': - parser->enclosure_nesting--; - yp_do_loop_stack_pop(parser); - - if ((parser->lex_modes.current->mode == YP_LEX_EMBEXPR) && (parser->brace_nesting == 0)) { - lex_mode_pop(parser); - LEX(YP_TOKEN_EMBEXPR_END); - } - - parser->brace_nesting--; - lex_state_set(parser, YP_LEX_STATE_END); - LEX(YP_TOKEN_BRACE_RIGHT); - - // * ** **= *= - case '*': { - if (match(parser, '*')) { - if (match(parser, '=')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_STAR_STAR_EQUAL); - } - - yp_token_type_t type = YP_TOKEN_STAR_STAR; - - if (lex_state_spcarg_p(parser, space_seen) || lex_state_beg_p(parser)) { - type = YP_TOKEN_USTAR_STAR; - } - - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - lex_state_set(parser, YP_LEX_STATE_BEG); - } - - LEX(type); - } - - if (match(parser, '=')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_STAR_EQUAL); - } - - yp_token_type_t type = YP_TOKEN_STAR; - - if (lex_state_spcarg_p(parser, space_seen)) { - yp_diagnostic_list_append(&parser->warning_list, parser->current.start, parser->current.end, "`*' interpreted as argument prefix"); - type = YP_TOKEN_USTAR; - } else if (lex_state_beg_p(parser)) { - type = YP_TOKEN_USTAR; - } - - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - lex_state_set(parser, YP_LEX_STATE_BEG); - } - - LEX(type); - } - - // ! != !~ !@ - case '!': - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - if (match(parser, '@')) { - LEX(YP_TOKEN_BANG); - } - } else { - lex_state_set(parser, YP_LEX_STATE_BEG); - } - - if (match(parser, '=')) { - LEX(YP_TOKEN_BANG_EQUAL); - } - - if (match(parser, '~')) { - LEX(YP_TOKEN_BANG_TILDE); - } - - LEX(YP_TOKEN_BANG); - - // = => =~ == === =begin - case '=': - if (current_token_starts_line(parser) && strncmp(parser->current.end, "begin", 5) == 0 && yp_char_is_whitespace(parser->current.end[5])) { - yp_token_type_t type = lex_embdoc(parser); - - if (type == YP_TOKEN_EOF) { - LEX(type); - } - - goto lex_next_token; - } - - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - lex_state_set(parser, YP_LEX_STATE_BEG); - } - - if (match(parser, '>')) { - LEX(YP_TOKEN_EQUAL_GREATER); - } - - if (match(parser, '~')) { - LEX(YP_TOKEN_EQUAL_TILDE); - } - - if (match(parser, '=')) { - LEX(match(parser, '=') ? YP_TOKEN_EQUAL_EQUAL_EQUAL : YP_TOKEN_EQUAL_EQUAL); - } - - LEX(YP_TOKEN_EQUAL); - - // < << <<= <= <=> - case '<': - if (match(parser, '<')) { - if ( - !lex_state_p(parser, YP_LEX_STATE_DOT | YP_LEX_STATE_CLASS) && - !lex_state_end_p(parser) && - (!lex_state_p(parser, YP_LEX_STATE_ARG_ANY) || lex_state_p(parser, YP_LEX_STATE_LABELED) || space_seen) - ) { - const char *end = parser->current.end; - - yp_heredoc_quote_t quote = YP_HEREDOC_QUOTE_NONE; - yp_heredoc_indent_t indent = YP_HEREDOC_INDENT_NONE; - - if (match(parser, '-')) { - indent = YP_HEREDOC_INDENT_DASH; - } - else if (match(parser, '~')) { - indent = YP_HEREDOC_INDENT_TILDE; - } - - if (match(parser, '`')) { - quote = YP_HEREDOC_QUOTE_BACKTICK; - } - else if (match(parser, '"')) { - quote = YP_HEREDOC_QUOTE_DOUBLE; - } - else if (match(parser, '\'')) { - quote = YP_HEREDOC_QUOTE_SINGLE; - } - - const char *ident_start = parser->current.end; - size_t width = 0; - - if (quote == YP_HEREDOC_QUOTE_NONE && (width = char_is_identifier(parser, parser->current.end)) == 0) { - parser->current.end = end; - } else { - if (quote == YP_HEREDOC_QUOTE_NONE) { - parser->current.end += width; - - while ((width = char_is_identifier(parser, parser->current.end))) { - parser->current.end += width; - } - } else { - // If we have quotes, then we're going to go until we find the - // end quote. - while (parser->current.end < parser->end && quote != (yp_heredoc_quote_t) (*parser->current.end)) { - parser->current.end++; - } - } - - size_t ident_length = (size_t) (parser->current.end - ident_start); - if (quote != YP_HEREDOC_QUOTE_NONE && !match(parser, quote)) { - // TODO: handle unterminated heredoc - } - - lex_mode_push(parser, (yp_lex_mode_t) { - .mode = YP_LEX_HEREDOC, - .as.heredoc = { - .ident_start = ident_start, - .ident_length = ident_length, - .next_start = parser->current.end, - .quote = quote, - .indent = indent - } - }); - - if (parser->heredoc_end == NULL) { - const char *body_start = (const char *) memchr(parser->current.end, '\n', (size_t) (parser->end - parser->current.end)); - - if (body_start == NULL) { - // If there is no newline after the heredoc identifier, then - // this is not a valid heredoc declaration. In this case we - // will add an error, but we will still return a heredoc - // start. - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unterminated heredoc."); - body_start = parser->end; - } else { - // Otherwise, we want to indicate that the body of the - // heredoc starts on the character after the next newline. - body_start++; - } - - parser->next_start = body_start; - } else { - parser->next_start = parser->heredoc_end; - } - - LEX(YP_TOKEN_HEREDOC_START); - } - } - - if (match(parser, '=')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_LESS_LESS_EQUAL); - } - - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - if (lex_state_p(parser, YP_LEX_STATE_CLASS)) parser->command_start = true; - lex_state_set(parser, YP_LEX_STATE_BEG); - } - - LEX(YP_TOKEN_LESS_LESS); - } - - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - if (lex_state_p(parser, YP_LEX_STATE_CLASS)) parser->command_start = true; - lex_state_set(parser, YP_LEX_STATE_BEG); - } - - if (match(parser, '=')) { - if (match(parser, '>')) { - LEX(YP_TOKEN_LESS_EQUAL_GREATER); - } - - LEX(YP_TOKEN_LESS_EQUAL); - } - - LEX(YP_TOKEN_LESS); - - // > >> >>= >= - case '>': - if (match(parser, '>')) { - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - lex_state_set(parser, YP_LEX_STATE_BEG); - } - LEX(match(parser, '=') ? YP_TOKEN_GREATER_GREATER_EQUAL : YP_TOKEN_GREATER_GREATER); - } - - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - lex_state_set(parser, YP_LEX_STATE_BEG); - } - - LEX(match(parser, '=') ? YP_TOKEN_GREATER_EQUAL : YP_TOKEN_GREATER); - - // double-quoted string literal - case '"': { - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_STRING, - .as.string.incrementor = '\0', - .as.string.terminator = '"', - .as.string.nesting = 0, - .as.string.interpolation = true, - .as.string.label_allowed = (lex_state_p(parser, YP_LEX_STATE_LABEL | YP_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser) - }; - - lex_mode_push(parser, lex_mode); - LEX(YP_TOKEN_STRING_BEGIN); - } - - // xstring literal - case '`': { - if (lex_state_p(parser, YP_LEX_STATE_FNAME)) { - lex_state_set(parser, YP_LEX_STATE_ENDFN); - LEX(YP_TOKEN_BACKTICK); - } - - if (lex_state_p(parser, YP_LEX_STATE_DOT)) { - if (previous_command_start) { - lex_state_set(parser, YP_LEX_STATE_CMDARG); - } else { - lex_state_set(parser, YP_LEX_STATE_ARG); - } - - LEX(YP_TOKEN_BACKTICK); - } - - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_STRING, - .as.string.incrementor = '\0', - .as.string.terminator = '`', - .as.string.nesting = 0, - .as.string.interpolation = true, - .as.string.label_allowed = false - }; - - lex_mode_push(parser, lex_mode); - LEX(YP_TOKEN_BACKTICK); - } - - // single-quoted string literal - case '\'': { - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_STRING, - .as.string.incrementor = '\0', - .as.string.terminator = '\'', - .as.string.nesting = 0, - .as.string.interpolation = false, - .as.string.label_allowed = (lex_state_p(parser, YP_LEX_STATE_LABEL | YP_LEX_STATE_ENDFN) && !previous_command_start) || lex_state_arg_p(parser) - }; - - lex_mode_push(parser, lex_mode); - LEX(YP_TOKEN_STRING_BEGIN); - } - - // ? character literal - case '?': - LEX(lex_question_mark(parser)); - - // & && &&= &= - case '&': - if (match(parser, '&')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - - if (match(parser, '=')) { - LEX(YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL); - } - - LEX(YP_TOKEN_AMPERSAND_AMPERSAND); - } - - if (match(parser, '=')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_AMPERSAND_EQUAL); - } - - if (match(parser, '.')) { - lex_state_set(parser, YP_LEX_STATE_DOT); - LEX(YP_TOKEN_AMPERSAND_DOT); - } - - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - lex_state_set(parser, YP_LEX_STATE_BEG); - } - - LEX(YP_TOKEN_AMPERSAND); - - // | || ||= |= - case '|': - if (match(parser, '|')) { - if (match(parser, '=')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_PIPE_PIPE_EQUAL); - } - - if (lex_state_p(parser, YP_LEX_STATE_BEG)) { - parser->current.end--; - LEX(YP_TOKEN_PIPE); - } - - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_PIPE_PIPE); - } - - if (match(parser, '=')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_PIPE_EQUAL); - } - - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); - } - - LEX(YP_TOKEN_PIPE); - - // + += +@ - case '+': { - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - - if (match(parser, '@')) { - LEX(YP_TOKEN_UPLUS); - } - - LEX(YP_TOKEN_PLUS); - } - - if (match(parser, '=')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_PLUS_EQUAL); - } - - bool spcarg = lex_state_spcarg_p(parser, space_seen); - if (spcarg) { - yp_diagnostic_list_append( - &parser->warning_list, - parser->current.start, - parser->current.end, - "ambiguous first argument; put parentheses or a space even after `+` operator" - ); - } - - if (lex_state_beg_p(parser) || spcarg) { - lex_state_set(parser, YP_LEX_STATE_BEG); - - if (yp_char_is_decimal_digit(peek(parser))) { - parser->current.end++; - yp_token_type_t type = lex_numeric(parser); - lex_state_set(parser, YP_LEX_STATE_END); - LEX(type); - } - - LEX(YP_TOKEN_UPLUS); - } - - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_PLUS); - } - - // - -= -@ - case '-': { - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - - if (match(parser, '@')) { - LEX(YP_TOKEN_UMINUS); - } - - LEX(YP_TOKEN_MINUS); - } - - if (match(parser, '=')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_MINUS_EQUAL); - } - - if (match(parser, '>')) { - lex_state_set(parser, YP_LEX_STATE_ENDFN); - LEX(YP_TOKEN_MINUS_GREATER); - } - - bool spcarg = lex_state_spcarg_p(parser, space_seen); - if (spcarg) { - yp_diagnostic_list_append( - &parser->warning_list, - parser->current.start, - parser->current.end, - "ambiguous first argument; put parentheses or a space even after `-` operator" - ); - } - - if (lex_state_beg_p(parser) || spcarg) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(yp_char_is_decimal_digit(peek(parser)) ? YP_TOKEN_UMINUS_NUM : YP_TOKEN_UMINUS); - } - - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_MINUS); - } - - // . .. ... - case '.': { - bool beg_p = lex_state_beg_p(parser); - - if (match(parser, '.')) { - if (match(parser, '.')) { - // If we're _not_ inside a range within default parameters - if ( - !context_p(parser, YP_CONTEXT_DEFAULT_PARAMS) && - context_p(parser, YP_CONTEXT_DEF_PARAMS) - ) { - if (lex_state_p(parser, YP_LEX_STATE_END)) { - lex_state_set(parser, YP_LEX_STATE_BEG); - } else { - lex_state_set(parser, YP_LEX_STATE_ENDARG); - } - LEX(YP_TOKEN_UDOT_DOT_DOT); - } - - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(beg_p ? YP_TOKEN_UDOT_DOT_DOT : YP_TOKEN_DOT_DOT_DOT); - } - - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(beg_p ? YP_TOKEN_UDOT_DOT : YP_TOKEN_DOT_DOT); - } - - lex_state_set(parser, YP_LEX_STATE_DOT); - LEX(YP_TOKEN_DOT); - } - - // integer - case '0': - case '1': - case '2': - case '3': - case '4': - case '5': - case '6': - case '7': - case '8': - case '9': { - yp_token_type_t type = lex_numeric(parser); - lex_state_set(parser, YP_LEX_STATE_END); - LEX(type); - } - - // :: symbol - case ':': - if (match(parser, ':')) { - if (lex_state_beg_p(parser) || lex_state_p(parser, YP_LEX_STATE_CLASS) || (lex_state_p(parser, YP_LEX_STATE_ARG_ANY) && space_seen)) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_UCOLON_COLON); - } - - lex_state_set(parser, YP_LEX_STATE_DOT); - LEX(YP_TOKEN_COLON_COLON); - } - - if (lex_state_end_p(parser) || yp_char_is_whitespace(*parser->current.end) || (*parser->current.end == '#')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_COLON); - } - - if ((*parser->current.end == '"') || (*parser->current.end == '\'')) { - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_STRING, - .as.string.incrementor = '\0', - .as.string.terminator = *parser->current.end, - .as.string.nesting = 0, - .as.string.interpolation = *parser->current.end == '"', - .as.string.label_allowed = false - }; - - lex_mode_push(parser, lex_mode); - parser->current.end++; - } - - lex_state_set(parser, YP_LEX_STATE_FNAME); - LEX(YP_TOKEN_SYMBOL_BEGIN); - - // / /= - case '/': - if (lex_state_beg_p(parser)) { - lex_mode_push(parser, (yp_lex_mode_t) { - .mode = YP_LEX_REGEXP, - .as.regexp.incrementor = '\0', - .as.regexp.terminator = '/', - .as.regexp.nesting = 0 - }); - - LEX(YP_TOKEN_REGEXP_BEGIN); - } - - if (match(parser, '=')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_SLASH_EQUAL); - } - - if (lex_state_spcarg_p(parser, space_seen)) { - yp_diagnostic_list_append(&parser->warning_list, parser->current.start, parser->current.end, "ambiguity between regexp and two divisions: wrap regexp in parentheses or add a space after `/' operator"); - - lex_mode_push(parser, (yp_lex_mode_t) { - .mode = YP_LEX_REGEXP, - .as.regexp.incrementor = '\0', - .as.regexp.terminator = '/', - .as.regexp.nesting = 0 - }); - - LEX(YP_TOKEN_REGEXP_BEGIN); - } - - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - lex_state_set(parser, YP_LEX_STATE_BEG); - } - - LEX(YP_TOKEN_SLASH); - - // ^ ^= - case '^': - if (lex_state_operator_p(parser)) { - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - lex_state_set(parser, YP_LEX_STATE_BEG); - } - LEX(match(parser, '=') ? YP_TOKEN_CARET_EQUAL : YP_TOKEN_CARET); - - // ~ ~@ - case '~': - if (lex_state_operator_p(parser)) { - (void) match(parser, '@'); - lex_state_set(parser, YP_LEX_STATE_ARG); - } else { - lex_state_set(parser, YP_LEX_STATE_BEG); - } - - LEX(YP_TOKEN_TILDE); - - // % %= %i %I %q %Q %w %W - case '%': { - // In a BEG state, if you encounter a % then you must be starting - // something. In this case if there is no subsequent character then - // we have an invalid token. - if (lex_state_beg_p(parser) && (parser->current.end >= parser->end)) { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "unexpected end of input"); - LEX(YP_TOKEN_STRING_BEGIN); - } - - if (!lex_state_beg_p(parser) && match(parser, '=')) { - lex_state_set(parser, YP_LEX_STATE_BEG); - LEX(YP_TOKEN_PERCENT_EQUAL); - } - else if( - lex_state_beg_p(parser) || - (lex_state_p(parser, YP_LEX_STATE_FITEM) && (*parser->current.end == 's')) || - lex_state_spcarg_p(parser, space_seen) - ) { - if (!parser->encoding.alnum_char(parser->current.end)) { - lex_mode_push(parser, (yp_lex_mode_t) { - .mode = YP_LEX_STRING, - .as.string.incrementor = incrementor(*parser->current.end), - .as.string.terminator = terminator(*parser->current.end), - .as.string.nesting = 0, - .as.string.interpolation = true, - .as.string.label_allowed = false - }); - - if (*parser->current.end == '\r') { - parser->current.end++; - } - - parser->current.end++; - LEX(YP_TOKEN_STRING_BEGIN); - } - - switch (*parser->current.end) { - case 'i': { - parser->current.end++; - const char delimiter = *parser->current.end++; - - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_LIST, - .as.list.incrementor = incrementor(delimiter), - .as.list.terminator = terminator(delimiter), - .as.list.nesting = 0, - .as.list.interpolation = false - }; - - lex_mode_push(parser, lex_mode); - LEX(YP_TOKEN_PERCENT_LOWER_I); - } - case 'I': { - parser->current.end++; - const char delimiter = *parser->current.end++; - - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_LIST, - .as.list.incrementor = incrementor(delimiter), - .as.list.terminator = terminator(delimiter), - .as.list.nesting = 0, - .as.list.interpolation = true - }; - - lex_mode_push(parser, lex_mode); - LEX(YP_TOKEN_PERCENT_UPPER_I); - } - case 'r': { - parser->current.end++; - - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_REGEXP, - .as.regexp.incrementor = incrementor(*parser->current.end), - .as.regexp.terminator = terminator(*parser->current.end), - .as.regexp.nesting = 0 - }; - - lex_mode_push(parser, lex_mode); - parser->current.end++; - - LEX(YP_TOKEN_REGEXP_BEGIN); - } - case 'q': { - parser->current.end++; - - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_STRING, - .as.string.incrementor = incrementor(*parser->current.end), - .as.string.terminator = terminator(*parser->current.end), - .as.string.nesting = 0, - .as.string.interpolation = false, - .as.string.label_allowed = false - }; - - lex_mode_push(parser, lex_mode); - parser->current.end++; - - LEX(YP_TOKEN_STRING_BEGIN); - } - case 'Q': { - parser->current.end++; - - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_STRING, - .as.string.incrementor = incrementor(*parser->current.end), - .as.string.terminator = terminator(*parser->current.end), - .as.string.nesting = 0, - .as.string.interpolation = true, - .as.string.label_allowed = false - }; - - lex_mode_push(parser, lex_mode); - parser->current.end++; - - LEX(YP_TOKEN_STRING_BEGIN); - } - case 's': { - parser->current.end++; - - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_STRING, - .as.string.incrementor = incrementor(*parser->current.end), - .as.string.terminator = terminator(*parser->current.end), - .as.string.nesting = 0, - .as.string.interpolation = false, - .as.string.label_allowed = false - }; - - lex_mode_push(parser, lex_mode); - lex_state_set(parser, YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM); - parser->current.end++; - - LEX(YP_TOKEN_SYMBOL_BEGIN); - } - case 'w': { - parser->current.end++; - const char delimiter = *parser->current.end++; - - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_LIST, - .as.list.incrementor = incrementor(delimiter), - .as.list.terminator = terminator(delimiter), - .as.list.nesting = 0, - .as.list.interpolation = false - }; - - lex_mode_push(parser, lex_mode); - LEX(YP_TOKEN_PERCENT_LOWER_W); - } - case 'W': { - parser->current.end++; - const char delimiter = *parser->current.end++; - - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_LIST, - .as.list.incrementor = incrementor(delimiter), - .as.list.terminator = terminator(delimiter), - .as.list.nesting = 0, - .as.list.interpolation = true - }; - - lex_mode_push(parser, lex_mode); - LEX(YP_TOKEN_PERCENT_UPPER_W); - } - case 'x': { - parser->current.end++; - - yp_lex_mode_t lex_mode = { - .mode = YP_LEX_STRING, - .as.string.incrementor = incrementor(*parser->current.end), - .as.string.terminator = terminator(*parser->current.end), - .as.string.nesting = 0, - .as.string.interpolation = true, - .as.string.label_allowed = false - }; - - lex_mode_push(parser, lex_mode); - parser->current.end++; - - LEX(YP_TOKEN_PERCENT_LOWER_X); - } - default: - // If we get to this point, then we have a % that is completely - // unparseable. In this case we'll just drop it from the parser - // and skip past it and hope that the next token is something - // that we can parse. - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "invalid %% token"); - goto lex_next_token; - } - } - - lex_state_set(parser, lex_state_operator_p(parser) ? YP_LEX_STATE_ARG : YP_LEX_STATE_BEG); - LEX(YP_TOKEN_PERCENT); - } - - // global variable - case '$': { - yp_token_type_t type = lex_global_variable(parser); - - // If we're lexing an embedded variable, then we need to pop back into - // the parent lex context. - if (parser->lex_modes.current->mode == YP_LEX_EMBVAR) { - lex_mode_pop(parser); - } - - lex_state_set(parser, YP_LEX_STATE_END); - LEX(type); - } - - // instance variable, class variable - case '@': - lex_state_set(parser, parser->lex_state & YP_LEX_STATE_FNAME ? YP_LEX_STATE_ENDFN : YP_LEX_STATE_END); - LEX(lex_at_variable(parser)); - - default: { - size_t width = char_is_identifier_start(parser, parser->current.start); - - // If this isn't the beginning of an identifier, then it's an invalid - // token as we've exhausted all of the other options. We'll skip past - // it and return the next token. - if (!width) { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid token."); - goto lex_next_token; - } - - parser->current.end = parser->current.start + width; - yp_token_type_t type = lex_identifier(parser, previous_command_start); - - // If we've hit a __END__ and it was at the start of the line or the - // start of the file and it is followed by either a \n or a \r\n, then - // this is the last token of the file. - if ( - ((parser->current.end - parser->current.start) == 7) && - current_token_starts_line(parser) && - (strncmp(parser->current.start, "__END__", 7) == 0) && - (*parser->current.end == '\n' || (*parser->current.end == '\r' && parser->current.end[1] == '\n')) - ) { - parser->current.end = parser->end; - parser->current.type = YP_TOKEN___END__; - parser_lex_callback(parser); - - yp_comment_t *comment = parser_comment(parser, YP_COMMENT___END__); - yp_list_append(&parser->comment_list, (yp_list_node_t *) comment); - - LEX(YP_TOKEN_EOF); - } - - yp_lex_state_t last_state = parser->lex_state; - - if (type == YP_TOKEN_IDENTIFIER || type == YP_TOKEN_CONSTANT) { - if (lex_state_p(parser, YP_LEX_STATE_BEG_ANY | YP_LEX_STATE_ARG_ANY | YP_LEX_STATE_DOT)) { - if (previous_command_start) { - lex_state_set(parser, YP_LEX_STATE_CMDARG); - } else { - lex_state_set(parser, YP_LEX_STATE_ARG); - } - } else if (parser->lex_state == YP_LEX_STATE_FNAME) { - lex_state_set(parser, YP_LEX_STATE_ENDFN); - } else { - lex_state_set(parser, YP_LEX_STATE_END); - } - } - - if ( - !(last_state & (YP_LEX_STATE_DOT | YP_LEX_STATE_FNAME)) && - (type == YP_TOKEN_IDENTIFIER) && - (yp_parser_local_depth(parser, &parser->current) != -1) - ) { - lex_state_set(parser, YP_LEX_STATE_END | YP_LEX_STATE_LABEL); - } - - LEX(type); - } - } - } - case YP_LEX_LIST: { - // First we'll set the beginning of the token. - parser->current.start = parser->current.end; - - // If there's any whitespace at the start of the list, then we're going to - // trim it off the beginning and create a new token. - size_t whitespace; - if ((whitespace = yp_strspn_whitespace(parser->current.end, parser->end - parser->current.end)) > 0) { - parser->current.end += whitespace; - LEX(YP_TOKEN_WORDS_SEP); - } - - // We'll check if we're at the end of the file. If we are, then we need to - // return the EOF token. - if (parser->current.end >= parser->end) { - LEX(YP_TOKEN_EOF); - } - - // These are the places where we need to split up the content of the list. - // We'll use strpbrk to find the first of these characters. - char breakpoints[] = "\\ \t\f\r\v\n\0\0\0"; - - // Now we'll add the terminator to the list of breakpoints. - size_t index = 7; - breakpoints[index++] = parser->lex_modes.current->as.list.terminator; - - // If interpolation is allowed, then we're going to check for the # - // character. Otherwise we'll only look for escapes and the terminator. - if (parser->lex_modes.current->as.list.interpolation) { - breakpoints[index++] = '#'; - } - - // If there is an incrementor, then we'll check for that as well. - if (parser->lex_modes.current->as.list.incrementor != '\0') { - breakpoints[index++] = parser->lex_modes.current->as.list.incrementor; - } - - const char *breakpoint = yp_strpbrk(parser->current.end, breakpoints, parser->end - parser->current.end); - while (breakpoint != NULL) { - switch (*breakpoint) { - case '\0': - // If we hit a null byte, skip directly past it. - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - break; - case '\\': { - // If we hit escapes, then we need to treat the next token - // literally. In this case we'll skip past the next character and - // find the next breakpoint. - size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, YP_UNESCAPE_ALL, false, &parser->error_list); - breakpoint = yp_strpbrk(breakpoint + difference, breakpoints, parser->end - (breakpoint + difference)); - break; - } - case ' ': - case '\t': - case '\f': - case '\r': - case '\v': - case '\n': - // If we've hit whitespace, then we must have received content by - // now, so we can return an element of the list. - parser->current.end = breakpoint; - LEX(YP_TOKEN_STRING_CONTENT); - case '#': { - // if # is the terminator, we need to fall into the default case - if (parser->lex_modes.current->as.list.terminator != '#') { - yp_token_type_t type = lex_interpolation(parser, breakpoint); - if (type != YP_TOKEN_NOT_PROVIDED) { - LEX(type); - } - - // If we haven't returned at this point then we had something - // that looked like an interpolated class or instance variable - // like "#@" but wasn't actually. In this case we'll just skip - // to the next breakpoint. - breakpoint = yp_strpbrk(parser->current.end, breakpoints, parser->end - parser->current.end); - break; - } - } - /* fallthrough */ - default: - if (*breakpoint == parser->lex_modes.current->as.list.incrementor) { - // If we've hit the incrementor, then we need to skip past it and - // find the next breakpoint. - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - parser->lex_modes.current->as.list.nesting++; - break; - } - - // In this case we've hit the terminator. - assert(*breakpoint == parser->lex_modes.current->as.list.terminator); - - // If this terminator doesn't actually close the list, then we need - // to continue on past it. - if (parser->lex_modes.current->as.list.nesting > 0) { - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - parser->lex_modes.current->as.list.nesting--; - break; - } - - // If we've hit the terminator and we've already skipped past - // content, then we can return a list node. - if (breakpoint > parser->current.start) { - parser->current.end = breakpoint; - LEX(YP_TOKEN_STRING_CONTENT); - } - - // Otherwise, switch back to the default state and return the end of - // the list. - parser->current.end = breakpoint + 1; - lex_mode_pop(parser); - - lex_state_set(parser, YP_LEX_STATE_END); - LEX(YP_TOKEN_STRING_END); - } - } - - // If we were unable to find a breakpoint, then this token hits the end of - // the file. - LEX(YP_TOKEN_EOF); - } - case YP_LEX_REGEXP: { - // First, we'll set to start of this token to be the current end. - parser->current.start = parser->current.end; - - // We'll check if we're at the end of the file. If we are, then we need to - // return the EOF token. - if (parser->current.end >= parser->end) { - LEX(YP_TOKEN_EOF); - } - - // These are the places where we need to split up the content of the - // regular expression. We'll use strpbrk to find the first of these - // characters. - char breakpoints[] = "\\#\0\0"; - - breakpoints[2] = parser->lex_modes.current->as.regexp.terminator; - if (parser->lex_modes.current->as.regexp.incrementor != '\0') { - breakpoints[3] = parser->lex_modes.current->as.regexp.incrementor; - } - - const char *breakpoint = yp_strpbrk(parser->current.end, breakpoints, parser->end - parser->current.end); - - while (breakpoint != NULL) { - switch (*breakpoint) { - case '\0': - // If we hit a null byte, skip directly past it. - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - break; - case '\\': { - // If we hit escapes, then we need to treat the next token - // literally. In this case we'll skip past the next character and - // find the next breakpoint. - size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, YP_UNESCAPE_ALL, false, &parser->error_list); - breakpoint = yp_strpbrk(breakpoint + difference, breakpoints, parser->end - (breakpoint + difference)); - break; - } - case '#': { - yp_token_type_t type = lex_interpolation(parser, breakpoint); - if (type != YP_TOKEN_NOT_PROVIDED) { - LEX(type); - } - - // We need to check if the terminator was # before skipping over - // to the next breakpoint - if (parser->lex_modes.current->as.regexp.terminator != '#') { - // If we haven't returned at this point then we had something - // that looked like an interpolated class or instance variable - // like "#@" but wasn't actually. In this case we'll just skip - // to the next breakpoint. - breakpoint = yp_strpbrk(parser->current.end, breakpoints, parser->end - parser->current.end); - break; - } - } - /* fallthrough */ - default: { - if (*breakpoint == parser->lex_modes.current->as.regexp.incrementor) { - // If we've hit the incrementor, then we need to skip past it and - // find the next breakpoint. - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - parser->lex_modes.current->as.regexp.nesting++; - break; - } - - assert(*breakpoint == parser->lex_modes.current->as.regexp.terminator); - - if (parser->lex_modes.current->as.regexp.nesting > 0) { - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - parser->lex_modes.current->as.regexp.nesting--; - break; - } - - // Here we've hit the terminator. If we have already consumed - // content then we need to return that content as string content - // first. - if (breakpoint > parser->current.start) { - parser->current.end = breakpoint; - LEX(YP_TOKEN_STRING_CONTENT); - } - - // Since we've hit the terminator of the regular expression, we now - // need to parse the options. - parser->current.end = breakpoint + 1; - parser->current.end += yp_strspn_regexp_option(parser->current.end, parser->end - parser->current.end); - - lex_mode_pop(parser); - lex_state_set(parser, YP_LEX_STATE_END); - LEX(YP_TOKEN_REGEXP_END); - } - } - } - - // At this point, the breakpoint is NULL which means we were unable to - // find anything before the end of the file. - LEX(YP_TOKEN_EOF); - } - case YP_LEX_STRING: { - // First, we'll set to start of this token to be the current end. - if (parser->next_start == NULL) { - parser->current.start = parser->current.end; - } else { - parser->current.start = parser->next_start; - parser->current.end = parser->next_start; - parser->next_start = NULL; - } - - // We'll check if we're at the end of the file. If we are, then we need to - // return the EOF token. - if (parser->current.end >= parser->end) { - LEX(YP_TOKEN_EOF); - } - - // These are the places where we need to split up the content of the - // string. We'll use strpbrk to find the first of these characters. - char breakpoints[] = "\n\\\0\0\0"; - size_t index = 2; - - // Now add in the terminator. - breakpoints[index++] = parser->lex_modes.current->as.string.terminator; - - // If interpolation is allowed, then we're going to check for the # - // character. Otherwise we'll only look for escapes and the terminator. - if (parser->lex_modes.current->as.string.interpolation) { - breakpoints[index++] = '#'; - } - - // If we have an incrementor, then we'll add that in as a breakpoint as - // well. - if (parser->lex_modes.current->as.string.incrementor != '\0') { - breakpoints[index++] = parser->lex_modes.current->as.string.incrementor; - } - - const char *breakpoint = yp_strpbrk(parser->current.end, breakpoints, parser->end - parser->current.end); - - while (breakpoint != NULL) { - // If we hit the incrementor, then we'll increment then nesting and - // continue lexing. - if ( - parser->lex_modes.current->as.string.incrementor != '\0' && - *breakpoint == parser->lex_modes.current->as.string.incrementor - ) { - parser->lex_modes.current->as.string.nesting++; - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - continue; - } - - // Note that we have to check the terminator here first because we could - // potentially be parsing a % string that has a # character as the - // terminator. - if (*breakpoint == parser->lex_modes.current->as.string.terminator) { - // If this terminator doesn't actually close the string, then we need - // to continue on past it. - if (parser->lex_modes.current->as.string.nesting > 0) { - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - parser->lex_modes.current->as.string.nesting--; - continue; - } - - // Here we've hit the terminator. If we have already consumed content - // then we need to return that content as string content first. - if (breakpoint > parser->current.start) { - parser->current.end = breakpoint; - LEX(YP_TOKEN_STRING_CONTENT); - } - - // Otherwise we need to switch back to the parent lex mode and - // return the end of the string. - if (*parser->current.end == '\r' && parser->current.end + 1 < parser->end && parser->current.end[1] == '\n') { - parser->current.end = breakpoint + 2; - } else { - parser->current.end = breakpoint + 1; - } - - if ( - parser->lex_modes.current->as.string.label_allowed && - (peek(parser) == ':') && - (peek_at(parser, 1) != ':') - ) { - parser->current.end++; - lex_state_set(parser, YP_LEX_STATE_ARG | YP_LEX_STATE_LABELED); - lex_mode_pop(parser); - LEX(YP_TOKEN_LABEL_END); - } - - lex_state_set(parser, YP_LEX_STATE_END); - lex_mode_pop(parser); - LEX(YP_TOKEN_STRING_END); - } - - // When we hit a newline, we need to flush any potential heredocs. Note - // that this has to happen after we check for the terminator in case the - // terminator is a newline character. - if (*breakpoint == '\n') { - if (parser->heredoc_end == NULL) { - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - continue; - } else { - parser->current.end = breakpoint + 1; - parser_flush_heredoc_end(parser); - LEX(YP_TOKEN_STRING_CONTENT); - } - } - - switch (*breakpoint) { - case '\0': - // Skip directly past the null character. - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - break; - case '\\': { - // If we hit escapes, then we need to treat the next token - // literally. In this case we'll skip past the next character and - // find the next breakpoint. - yp_unescape_type_t unescape_type = parser->lex_modes.current->as.string.interpolation ? YP_UNESCAPE_ALL : YP_UNESCAPE_MINIMAL; - size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, unescape_type, false, &parser->error_list); - breakpoint = yp_strpbrk(breakpoint + difference, breakpoints, parser->end - (breakpoint + difference)); - break; - } - case '#': { - yp_token_type_t type = lex_interpolation(parser, breakpoint); - if (type != YP_TOKEN_NOT_PROVIDED) { - LEX(type); - } - - // If we haven't returned at this point then we had something that - // looked like an interpolated class or instance variable like "#@" - // but wasn't actually. In this case we'll just skip to the next - // breakpoint. - breakpoint = yp_strpbrk(parser->current.end, breakpoints, parser->end - parser->current.end); - break; - } - default: - assert(false && "unreachable"); - } - } - - // If we've hit the end of the string, then this is an unterminated - // string. In that case we'll return the EOF token. - parser->current.end = parser->end; - LEX(YP_TOKEN_EOF); - } - case YP_LEX_HEREDOC: { - // First, we'll set to start of this token. - if (parser->next_start == NULL) { - parser->current.start = parser->current.end; - } else { - parser->current.start = parser->next_start; - parser->current.end = parser->next_start; - parser->next_start = NULL; - } - - // We'll check if we're at the end of the file. If we are, then we need to - // return the EOF token. - if (parser->current.end >= parser->end) { - LEX(YP_TOKEN_EOF); - } - - // Now let's grab the information about the identifier off of the current - // lex mode. - const char *ident_start = parser->lex_modes.current->as.heredoc.ident_start; - size_t ident_length = parser->lex_modes.current->as.heredoc.ident_length; - - // If we are immediately following a newline and we have hit the - // terminator, then we need to return the ending of the heredoc. - if (parser->current.start[-1] == '\n') { - const char *start = parser->current.start; - if (parser->lex_modes.current->as.heredoc.indent != YP_HEREDOC_INDENT_NONE) { - start += yp_strspn_inline_whitespace(start, parser->end - start); - } - - if (strncmp(start, ident_start, ident_length) == 0) { - bool matched = true; - bool at_end = false; - - if ((start + ident_length < parser->end) && (start[ident_length] == '\n')) { - parser->current.end = start + ident_length + 1; - } else if ((start + ident_length + 1 < parser->end) && (start[ident_length] == '\r') && (start[ident_length + 1] == '\n')) { - parser->current.end = start + ident_length + 2; - } else if (parser->end == (start + ident_length)) { - parser->current.end = start + ident_length; - at_end = true; - } else { - matched = false; - } - - if (matched) { - if (*parser->lex_modes.current->as.heredoc.next_start == '\\') { - parser->next_start = NULL; - } else { - parser->next_start = parser->lex_modes.current->as.heredoc.next_start; - parser->heredoc_end = parser->current.end; - } - - lex_mode_pop(parser); - if (!at_end) { - lex_state_set(parser, YP_LEX_STATE_END); - } - LEX(YP_TOKEN_HEREDOC_END); - } - } - } - - // Otherwise we'll be parsing string content. These are the places where - // we need to split up the content of the heredoc. We'll use strpbrk to - // find the first of these characters. - char breakpoints[] = "\n\\#"; - yp_heredoc_quote_t quote = parser->lex_modes.current->as.heredoc.quote; - if (quote == YP_HEREDOC_QUOTE_SINGLE) { - breakpoints[2] = '\0'; - } - - const char *breakpoint = yp_strpbrk(parser->current.end, breakpoints, parser->end - parser->current.end); - - while (breakpoint != NULL) { - switch (*breakpoint) { - case '\0': - // Skip directly past the null character. - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - break; - case '\n': { - if (parser->heredoc_end != NULL && (parser->heredoc_end > breakpoint)) { - parser_flush_heredoc_end(parser); - parser->current.end = breakpoint + 1; - LEX(YP_TOKEN_STRING_CONTENT); - } - - const char *start = breakpoint + 1; - if (parser->lex_modes.current->as.heredoc.indent != YP_HEREDOC_INDENT_NONE) { - start += yp_strspn_inline_whitespace(start, parser->end - start); - } - - // If we have hit a newline that is followed by a valid terminator, - // then we need to return the content of the heredoc here as string - // content. Then, the next time a token is lexed, it will match - // again and return the end of the heredoc. - if ( - (start + ident_length <= parser->end) && - (strncmp(start, ident_start, ident_length) == 0) - ) { - // Heredoc terminators must be followed by a newline or EOF to be valid. - if (start + ident_length == parser->end || start[ident_length] == '\n') { - parser->current.end = breakpoint + 1; - LEX(YP_TOKEN_STRING_CONTENT); - } - - // They can also be followed by a carriage return and then a - // newline. Be sure here that we don't accidentally read off the - // end. - if ( - (start + ident_length + 1 < parser->end) && - (start[ident_length] == '\r') && - (start[ident_length + 1] == '\n') - ) { - parser->current.end = breakpoint + 1; - LEX(YP_TOKEN_STRING_CONTENT); - } - } - - // Otherwise we hit a newline and it wasn't followed by a - // terminator, so we can continue parsing. - breakpoint = yp_strpbrk(breakpoint + 1, breakpoints, parser->end - (breakpoint + 1)); - break; - } - case '\\': { - // If we hit escapes, then we need to treat the next token - // literally. In this case we'll skip past the next character and - // find the next breakpoint. - if (breakpoint[1] == '\n') { - breakpoint++; - } else { - yp_unescape_type_t unescape_type = (quote == YP_HEREDOC_QUOTE_SINGLE) ? YP_UNESCAPE_MINIMAL : YP_UNESCAPE_ALL; - size_t difference = yp_unescape_calculate_difference(breakpoint, parser->end, unescape_type, false, &parser->error_list); - breakpoint = yp_strpbrk(breakpoint + difference, breakpoints, parser->end - (breakpoint + difference)); - } - break; - } - case '#': { - yp_token_type_t type = lex_interpolation(parser, breakpoint); - if (type != YP_TOKEN_NOT_PROVIDED) { - LEX(type); - } - - // If we haven't returned at this point then we had something - // that looked like an interpolated class or instance variable - // like "#@" but wasn't actually. In this case we'll just skip - // to the next breakpoint. - breakpoint = yp_strpbrk(parser->current.end, breakpoints, parser->end - parser->current.end); - break; - } - default: - assert(false && "unreachable"); - } - } - - // If we've hit the end of the string, then this is an unterminated - // heredoc. In that case we'll return the EOF token. - parser->current.end = parser->end; - LEX(YP_TOKEN_EOF); - } - } - - assert(false && "unreachable"); -} - -#undef LEX - -/******************************************************************************/ -/* Parse functions */ -/******************************************************************************/ - -// When we are parsing certain content, we need to unescape the content to -// provide to the consumers of the parser. The following functions accept a range -// of characters from the source and unescapes into the provided type. -// -// We have functions for unescaping regular expression nodes, string nodes, -// symbol nodes, and xstring nodes -static yp_regular_expression_node_t * -yp_regular_expression_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing, yp_unescape_type_t unescape_type) { - yp_regular_expression_node_t *node = yp_regular_expression_node_create(parser, opening, content, closing); - yp_unescape_manipulate_string(content->start, (size_t) (content->end - content->start), &node->unescaped, unescape_type, &parser->error_list); - return node; -} - -static yp_symbol_node_t * -yp_symbol_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing) { - yp_symbol_node_t *node = yp_symbol_node_create(parser, opening, content, closing); - yp_unescape_manipulate_string(content->start, (size_t) (content->end - content->start), &node->unescaped, YP_UNESCAPE_ALL, &parser->error_list); - return node; -} - -static yp_string_node_t * -yp_string_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing, yp_unescape_type_t unescape_type) { - yp_string_node_t *node = yp_string_node_create(parser, opening, content, closing); - yp_unescape_manipulate_string(content->start, (size_t) (content->end - content->start), &node->unescaped, unescape_type, &parser->error_list); - return node; -} - -static yp_x_string_node_t * -yp_xstring_node_create_and_unescape(yp_parser_t *parser, const yp_token_t *opening, const yp_token_t *content, const yp_token_t *closing) { - yp_x_string_node_t *node = yp_xstring_node_create(parser, opening, content, closing); - yp_unescape_manipulate_string(content->start, (size_t) (content->end - content->start), &node->unescaped, YP_UNESCAPE_ALL, &parser->error_list); - return node; -} - -// Returns true if the current token is of the specified type. -static inline bool -match_type_p(yp_parser_t *parser, yp_token_type_t type) { - return parser->current.type == type; -} - -// Returns true if the current token is of any of the specified types. -static bool -match_any_type_p(yp_parser_t *parser, size_t count, ...) { - va_list types; - va_start(types, count); - - for (size_t index = 0; index < count; index++) { - if (match_type_p(parser, va_arg(types, yp_token_type_t))) { - va_end(types); - return true; - } - } - - va_end(types); - return false; -} - -// These are the various precedence rules. Because we are using a Pratt parser, -// they are named binding power to represent the manner in which nodes are bound -// together in the stack. -// -// We increment by 2 because we want to leave room for the infix operators to -// specify their associativity by adding or subtracting one. -typedef enum { - YP_BINDING_POWER_UNSET = 0, // used to indicate this token cannot be used as an infix operator - YP_BINDING_POWER_STATEMENT = 2, - YP_BINDING_POWER_MODIFIER = 4, // if unless until while in - YP_BINDING_POWER_MODIFIER_RESCUE = 6, // rescue - YP_BINDING_POWER_COMPOSITION = 8, // and or - YP_BINDING_POWER_NOT = 10, // not - YP_BINDING_POWER_MATCH = 12, // => - YP_BINDING_POWER_DEFINED = 14, // defined? - YP_BINDING_POWER_ASSIGNMENT = 16, // = += -= *= /= %= &= |= ^= &&= ||= <<= >>= **= - YP_BINDING_POWER_TERNARY = 18, // ?: - YP_BINDING_POWER_RANGE = 20, // .. ... - YP_BINDING_POWER_LOGICAL_OR = 22, // || - YP_BINDING_POWER_LOGICAL_AND = 24, // && - YP_BINDING_POWER_EQUALITY = 26, // <=> == === != =~ !~ - YP_BINDING_POWER_COMPARISON = 28, // > >= < <= - YP_BINDING_POWER_BITWISE_OR = 30, // | ^ - YP_BINDING_POWER_BITWISE_AND = 32, // & - YP_BINDING_POWER_SHIFT = 34, // << >> - YP_BINDING_POWER_TERM = 36, // + - - YP_BINDING_POWER_FACTOR = 38, // * / % - YP_BINDING_POWER_UMINUS = 40, // -@ - YP_BINDING_POWER_EXPONENT = 42, // ** - YP_BINDING_POWER_UNARY = 44, // ! ~ +@ - YP_BINDING_POWER_INDEX = 46, // [] []= - YP_BINDING_POWER_CALL = 48, // :: . - YP_BINDING_POWER_MAX = 50 -} yp_binding_power_t; - -// This struct represents a set of binding powers used for a given token. They -// are combined in this way to make it easier to represent associativity. -typedef struct { - yp_binding_power_t left; - yp_binding_power_t right; - bool binary; -} yp_binding_powers_t; - -#define BINDING_POWER_ASSIGNMENT { YP_BINDING_POWER_UNARY, YP_BINDING_POWER_ASSIGNMENT, true } -#define LEFT_ASSOCIATIVE(precedence) { precedence, precedence + 1, true } -#define RIGHT_ASSOCIATIVE(precedence) { precedence, precedence, true } -#define RIGHT_ASSOCIATIVE_UNARY(precedence) { precedence, precedence, false } - -yp_binding_powers_t yp_binding_powers[YP_TOKEN_MAXIMUM] = { - // if unless until while in rescue - [YP_TOKEN_KEYWORD_IF_MODIFIER] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MODIFIER), - [YP_TOKEN_KEYWORD_UNLESS_MODIFIER] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MODIFIER), - [YP_TOKEN_KEYWORD_UNTIL_MODIFIER] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MODIFIER), - [YP_TOKEN_KEYWORD_WHILE_MODIFIER] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MODIFIER), - [YP_TOKEN_KEYWORD_IN] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MODIFIER), - - // rescue modifier - [YP_TOKEN_KEYWORD_RESCUE_MODIFIER] = { - YP_BINDING_POWER_ASSIGNMENT, - YP_BINDING_POWER_MODIFIER_RESCUE + 1, - true - }, - - // and or - [YP_TOKEN_KEYWORD_AND] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_COMPOSITION), - [YP_TOKEN_KEYWORD_OR] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_COMPOSITION), - - // => - [YP_TOKEN_EQUAL_GREATER] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_MATCH), - - // &&= &= ^= = >>= <<= -= %= |= += /= *= **= - [YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_AMPERSAND_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_CARET_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_GREATER_GREATER_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_LESS_LESS_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_MINUS_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_PERCENT_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_PIPE_PIPE_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_PLUS_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_SLASH_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT, - [YP_TOKEN_STAR_STAR_EQUAL] = BINDING_POWER_ASSIGNMENT, - - // ?: - [YP_TOKEN_QUESTION_MARK] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_TERNARY), - - // .. ... - [YP_TOKEN_DOT_DOT] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_RANGE), - [YP_TOKEN_DOT_DOT_DOT] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_RANGE), - - // || - [YP_TOKEN_PIPE_PIPE] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_LOGICAL_OR), - - // && - [YP_TOKEN_AMPERSAND_AMPERSAND] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_LOGICAL_AND), - - // != !~ == === =~ <=> - [YP_TOKEN_BANG_EQUAL] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), - [YP_TOKEN_BANG_TILDE] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), - [YP_TOKEN_EQUAL_EQUAL] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), - [YP_TOKEN_EQUAL_EQUAL_EQUAL] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), - [YP_TOKEN_EQUAL_TILDE] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), - [YP_TOKEN_LESS_EQUAL_GREATER] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EQUALITY), - - // > >= < <= - [YP_TOKEN_GREATER] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_COMPARISON), - [YP_TOKEN_GREATER_EQUAL] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_COMPARISON), - [YP_TOKEN_LESS] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_COMPARISON), - [YP_TOKEN_LESS_EQUAL] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_COMPARISON), - - // ^ | - [YP_TOKEN_CARET] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_BITWISE_OR), - [YP_TOKEN_PIPE] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_BITWISE_OR), - - // & - [YP_TOKEN_AMPERSAND] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_BITWISE_AND), - - // >> << - [YP_TOKEN_GREATER_GREATER] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_SHIFT), - [YP_TOKEN_LESS_LESS] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_SHIFT), - - // - + - [YP_TOKEN_MINUS] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_TERM), - [YP_TOKEN_PLUS] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_TERM), - - // % / * - [YP_TOKEN_PERCENT] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_FACTOR), - [YP_TOKEN_SLASH] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_FACTOR), - [YP_TOKEN_STAR] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_FACTOR), - [YP_TOKEN_USTAR] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_FACTOR), - - // -@ - [YP_TOKEN_UMINUS] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UMINUS), - [YP_TOKEN_UMINUS_NUM] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UMINUS), - - // ** - [YP_TOKEN_STAR_STAR] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_EXPONENT), - [YP_TOKEN_USTAR_STAR] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UNARY), - - // ! ~ +@ - [YP_TOKEN_BANG] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UNARY), - [YP_TOKEN_TILDE] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UNARY), - [YP_TOKEN_UPLUS] = RIGHT_ASSOCIATIVE_UNARY(YP_BINDING_POWER_UNARY), - - // [ - [YP_TOKEN_BRACKET_LEFT] = LEFT_ASSOCIATIVE(YP_BINDING_POWER_INDEX), - - // :: . &. - [YP_TOKEN_COLON_COLON] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_CALL), - [YP_TOKEN_DOT] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_CALL), - [YP_TOKEN_AMPERSAND_DOT] = RIGHT_ASSOCIATIVE(YP_BINDING_POWER_CALL) -}; - -#undef BINDING_POWER_ASSIGNMENT -#undef LEFT_ASSOCIATIVE -#undef RIGHT_ASSOCIATIVE -#undef RIGHT_ASSOCIATIVE_UNARY - -// If the current token is of the specified type, lex forward by one token and -// return true. Otherwise, return false. For example: -// -// if (accept(parser, YP_TOKEN_COLON)) { ... } -// -static bool -accept(yp_parser_t *parser, yp_token_type_t type) { - if (match_type_p(parser, type)) { - parser_lex(parser); - return true; - } - return false; -} - -// If the current token is of any of the specified types, lex forward by one -// token and return true. Otherwise, return false. For example: -// -// if (accept_any(parser, 2, YP_TOKEN_COLON, YP_TOKEN_SEMICOLON)) { ... } -// -static bool -accept_any(yp_parser_t *parser, size_t count, ...) { - va_list types; - va_start(types, count); - - for (size_t index = 0; index < count; index++) { - if (match_type_p(parser, va_arg(types, yp_token_type_t))) { - parser_lex(parser); - va_end(types); - return true; - } - } - - va_end(types); - return false; -} - -// This function indicates that the parser expects a token in a specific -// position. For example, if you're parsing a BEGIN block, you know that a { is -// expected immediately after the keyword. In that case you would call this -// function to indicate that that token should be found. -// -// If we didn't find the token that we were expecting, then we're going to add -// an error to the parser's list of errors (to indicate that the tree is not -// valid) and create an artificial token instead. This allows us to recover from -// the fact that the token isn't present and continue parsing. -static void -expect(yp_parser_t *parser, yp_token_type_t type, const char *message) { - if (accept(parser, type)) return; - - yp_diagnostic_list_append(&parser->error_list, parser->previous.end, parser->previous.end, message); - - parser->previous = - (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; -} - -static void -expect_any(yp_parser_t *parser, const char*message, size_t count, ...) { - va_list types; - va_start(types, count); - - for (size_t index = 0; index < count; index++) { - if (accept(parser, va_arg(types, yp_token_type_t))) { - va_end(types); - return; - } - } - - va_end(types); - - yp_diagnostic_list_append(&parser->error_list, parser->previous.end, parser->previous.end, message); - parser->previous = - (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; -} - -static yp_node_t * -parse_expression(yp_parser_t *parser, yp_binding_power_t binding_power, const char *message); - -// This function controls whether or not we will attempt to parse an expression -// beginning at the subsequent token. It is used when we are in a context where -// an expression is optional. -// -// For example, looking at a range object when we've already lexed the operator, -// we need to know if we should attempt to parse an expression on the right. -// -// For another example, if we've parsed an identifier or a method call and we do -// not have parentheses, then the next token may be the start of an argument or -// it may not. -// -// CRuby parsers that are generated would resolve this by using a lookahead and -// potentially backtracking. We attempt to do this by just looking at the next -// token and making a decision based on that. I am not sure if this is going to -// work in all cases, it may need to be refactored later. But it appears to work -// for now. -static inline bool -token_begins_expression_p(yp_token_type_t type) { - switch (type) { - case YP_TOKEN_EQUAL_GREATER: - case YP_TOKEN_KEYWORD_IN: - // We need to special case this because it is a binary operator that - // should not be marked as beginning an expression. - return false; - case YP_TOKEN_BRACE_RIGHT: - case YP_TOKEN_BRACKET_RIGHT: - case YP_TOKEN_COLON: - case YP_TOKEN_COMMA: - case YP_TOKEN_EMBEXPR_END: - case YP_TOKEN_EOF: - case YP_TOKEN_LAMBDA_BEGIN: - case YP_TOKEN_KEYWORD_DO: - case YP_TOKEN_KEYWORD_DO_LOOP: - case YP_TOKEN_KEYWORD_END: - case YP_TOKEN_KEYWORD_ELSE: - case YP_TOKEN_KEYWORD_ELSIF: - case YP_TOKEN_KEYWORD_THEN: - case YP_TOKEN_KEYWORD_RESCUE: - case YP_TOKEN_KEYWORD_WHEN: - case YP_TOKEN_NEWLINE: - case YP_TOKEN_PARENTHESIS_RIGHT: - case YP_TOKEN_SEMICOLON: - // The reason we need this short-circuit is because we're using the - // binding powers table to tell us if the subsequent token could - // potentially be the start of an expression . If there _is_ a binding - // power for one of these tokens, then we should remove it from this list - // and let it be handled by the default case below. - assert(yp_binding_powers[type].left == YP_BINDING_POWER_UNSET); - return false; - case YP_TOKEN_UCOLON_COLON: - case YP_TOKEN_UMINUS: - case YP_TOKEN_UMINUS_NUM: - case YP_TOKEN_UPLUS: - case YP_TOKEN_BANG: - case YP_TOKEN_TILDE: - case YP_TOKEN_UDOT_DOT: - case YP_TOKEN_UDOT_DOT_DOT: - // These unary tokens actually do have binding power associated with them - // so that we can correctly place them into the precedence order. But we - // want them to be marked as beginning an expression, so we need to - // special case them here. - return true; - default: - return yp_binding_powers[type].left == YP_BINDING_POWER_UNSET; - } -} - -// Parse an expression with the given binding power that may be optionally -// prefixed by the * operator. -static yp_node_t * -parse_starred_expression(yp_parser_t *parser, yp_binding_power_t binding_power, const char *message) { - if (accept(parser, YP_TOKEN_USTAR)) { - yp_token_t operator = parser->previous; - yp_node_t *expression = parse_expression(parser, binding_power, "Expected expression after `*'."); - return (yp_node_t *) yp_splat_node_create(parser, &operator, expression); - } - - return parse_expression(parser, binding_power, message); -} - -// Convert the given node into a valid target node. -static yp_node_t * -parse_target(yp_parser_t *parser, yp_node_t *target, yp_token_t *operator, yp_node_t *value) { - switch (target->type) { - case YP_NODE_MISSING_NODE: - return target; - case YP_NODE_CLASS_VARIABLE_READ_NODE: { - yp_class_variable_write_node_t *write_node = yp_class_variable_read_node_to_class_variable_write_node(parser, (yp_class_variable_read_node_t *) target, operator, value); - yp_node_destroy(parser, target); - return (yp_node_t *) write_node; - } - case YP_NODE_CONSTANT_PATH_NODE: - case YP_NODE_CONSTANT_READ_NODE: - return (yp_node_t *) yp_constant_path_write_node_create(parser, target, operator, value); - case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { - yp_token_t name = ((yp_global_variable_read_node_t *) target)->name; - yp_global_variable_write_node_t *result = yp_global_variable_write_node_create(parser, &name, operator, value); - if ((name.type == YP_TOKEN_BACK_REFERENCE) || (name.type == YP_TOKEN_NTH_REFERENCE)) { - yp_diagnostic_list_append( - &parser->error_list, - name.start, - name.end, - "Can't set variable" - ); - } - yp_node_destroy(parser, target); - return (yp_node_t *) result; - } - case YP_NODE_LOCAL_VARIABLE_READ_NODE: { - yp_location_t name_loc = target->location; - uint32_t depth = ((yp_local_variable_read_node_t *) target)->depth; - yp_node_destroy(parser, target); - - return (yp_node_t *) yp_local_variable_write_node_create(parser, &name_loc, value, operator, depth); - } - case YP_NODE_INSTANCE_VARIABLE_READ_NODE: { - yp_node_t *write_node = (yp_node_t *) yp_instance_variable_write_node_create(parser, (yp_instance_variable_read_node_t *) target, operator, value); - yp_node_destroy(parser, target); - return write_node; - } - case YP_NODE_MULTI_WRITE_NODE: { - yp_multi_write_node_t *multi_write = (yp_multi_write_node_t *) target; - multi_write->operator = *operator; - - if (value != NULL) { - multi_write->value = value; - multi_write->base.location.end = value->location.end; - } - - return (yp_node_t *) multi_write; - } - case YP_NODE_SPLAT_NODE: { - yp_splat_node_t *splat = (yp_splat_node_t *) target; - - if (splat->expression != NULL) { - splat->expression = parse_target(parser, splat->expression, operator, value); - } - - yp_location_t location = { .start = NULL, .end = NULL }; - yp_multi_write_node_t *multi_write = yp_multi_write_node_create(parser, operator, value, &location, &location); - yp_multi_write_node_targets_append(multi_write, (yp_node_t *) splat); - - return (yp_node_t *) multi_write; - } - case YP_NODE_CALL_NODE: { - yp_call_node_t *call = (yp_call_node_t *) target; - // If we have no arguments to the call node and we need this to be a - // target then this is either a method call or a local variable write. - if ( - (call->opening.type == YP_TOKEN_NOT_PROVIDED) && - (call->arguments == NULL) && - (call->block == NULL) - ) { - if (call->receiver == NULL) { - // When we get here, we have a local variable write, because it - // was previously marked as a method call but now we have an =. - // This looks like: - // - // foo = 1 - // - // When it was parsed in the prefix position, foo was seen as a - // method call with no receiver and no arguments. Now we have an - // =, so we know it's a local variable write. - yp_token_t name = call->message; - yp_parser_local_add(parser, &name); - yp_node_destroy(parser, target); - - yp_location_t name_loc = { .start = name.start, .end = name.end }; - target = (yp_node_t *) yp_local_variable_write_node_create(parser, &name_loc, value, operator, 0); - - if (token_is_numbered_parameter(&name)) { - yp_diagnostic_list_append(&parser->error_list, name.start, name.end, "reserved for numbered parameter"); - } - - return target; - } - - // When we get here, we have a method call, because it was - // previously marked as a method call but now we have an =. This - // looks like: - // - // foo.bar = 1 - // - // When it was parsed in the prefix position, foo.bar was seen as a - // method call with no arguments. Now we have an =, so we know it's - // a method call with an argument. In this case we will create the - // arguments node, parse the argument, and add it to the list. - if (value) { - yp_arguments_node_t *arguments = yp_arguments_node_create(parser); - call->arguments = arguments; - yp_arguments_node_arguments_append(arguments, value); - } - - // The method name needs to change. If we previously had foo, we now - // need foo=. In this case we'll allocate a new owned string, copy - // the previous method name in, and append an =. - size_t length = yp_string_length(&call->name); - char *name = malloc(length + 2); - sprintf(name, "%.*s=", (int) length, yp_string_source(&call->name)); - - // Now switch the name to the new string. - yp_string_free(&call->name); - yp_string_owned_init(&call->name, name, length + 1); - - return target; - } - - // If there is no call operator and the message is "[]" then this is - // an aref expression, and we can transform it into an aset - // expression. - if ( - (call->call_operator.type == YP_TOKEN_NOT_PROVIDED) && - (call->message.type == YP_TOKEN_BRACKET_LEFT_RIGHT) && - (call->block == NULL) - ) { - call->message.type = YP_TOKEN_BRACKET_LEFT_RIGHT_EQUAL; - - if (value != NULL) { - if (call->arguments == NULL) { - call->arguments = yp_arguments_node_create(parser); - } - - yp_arguments_node_arguments_append(call->arguments, value); - target->location.end = value->location.end; - } - - // Free the previous name and replace it with "[]=". - yp_string_free(&call->name); - yp_string_constant_init(&call->name, "[]=", 3); - return target; - } - - // If there are arguments on the call node, then it can't be a method - // call ending with = or a local variable write, so it must be a - // syntax error. In this case we'll fall through to our default - // handling. - } - /* fallthrough */ - default: - // In this case we have a node that we don't know how to convert into a - // target. We need to treat it as an error. For now, we'll mark it as an - // error and just skip right past it. - yp_diagnostic_list_append(&parser->error_list, operator->start, operator->end, "Unexpected `='."); - return target; - } -} - -// Parse a list of targets for assignment. This is used in the case of a for -// loop or a multi-assignment. For example, in the following code: -// -// for foo, bar in baz -// ^^^^^^^^ -// -// The targets are `foo` and `bar`. This function will either return a single -// target node or a multi-target node. -static yp_node_t * -parse_targets(yp_parser_t *parser, yp_node_t *first_target, yp_binding_power_t binding_power) { - yp_token_t operator = not_provided(parser); - - // The first_target parameter can be NULL in the case that we're parsing a - // location that we know requires a multi write, as in the case of a for loop. - // In this case we will set up the parsing loop slightly differently. - if (first_target != NULL) { - first_target = parse_target(parser, first_target, &operator, NULL); - - if (!match_type_p(parser, YP_TOKEN_COMMA)) { - return first_target; - } - } - - yp_location_t lparen_loc = { .start = NULL, .end = NULL }; - yp_multi_write_node_t *result = yp_multi_write_node_create(parser, &operator, NULL, &lparen_loc, &lparen_loc); - - if (first_target != NULL) { - yp_multi_write_node_targets_append(result, first_target); - } - - bool has_splat = false; - - if (first_target == NULL || accept(parser, YP_TOKEN_COMMA)) { - do { - if (accept(parser, YP_TOKEN_USTAR)) { - // Here we have a splat operator. It can have a name or be anonymous. It - // can be the final target or be in the middle if there haven't been any - // others yet. - - if (has_splat) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Multiple splats in multi-assignment."); - } - - yp_token_t star_operator = parser->previous; - yp_node_t *name = NULL; - - if (token_begins_expression_p(parser->current.type)) { - yp_token_t operator = not_provided(parser); - name = parse_expression(parser, binding_power, "Expected an expression after '*'."); - name = parse_target(parser, name, &operator, NULL); - } - - yp_node_t *splat = (yp_node_t *) yp_splat_node_create(parser, &star_operator, name); - yp_multi_write_node_targets_append(result, splat); - has_splat = true; - } else if (accept(parser, YP_TOKEN_PARENTHESIS_LEFT)) { - // Here we have a parenthesized list of targets. We'll recurse down into - // the parentheses by calling parse_targets again and then finish out - // the node when it returns. - - yp_token_t lparen = parser->previous; - yp_node_t *first_child_target = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected an expression after '('."); - yp_node_t *child_target = parse_targets(parser, first_child_target, YP_BINDING_POWER_STATEMENT); - - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected an ')' after multi-assignment."); - yp_token_t rparen = parser->previous; - - if (child_target->type == YP_NODE_MULTI_WRITE_NODE && first_target == NULL && result->targets.size == 0) { - yp_node_destroy(parser, (yp_node_t *) result); - result = (yp_multi_write_node_t *) child_target; - result->base.location.start = lparen.start; - result->base.location.end = rparen.end; - result->lparen_loc = (yp_location_t) { .start = lparen.start, .end = lparen.end }; - result->rparen_loc = (yp_location_t) { .start = rparen.start, .end = rparen.end }; - } else { - yp_multi_write_node_t *target; - - if (child_target->type == YP_NODE_MULTI_WRITE_NODE) { - target = (yp_multi_write_node_t *) child_target; - target->lparen_loc = (yp_location_t) { .start = lparen.start, .end = lparen.end }; - target->rparen_loc = (yp_location_t) { .start = rparen.start, .end = rparen.end }; - } else { - yp_token_t operator = not_provided(parser); - - target = yp_multi_write_node_create( - parser, - &operator, - NULL, - &(yp_location_t) { .start = lparen.start, .end = lparen.end }, - &(yp_location_t) { .start = rparen.start, .end = rparen.end } - ); - - yp_multi_write_node_targets_append(target, child_target); - } - - target->base.location.end = rparen.end; - yp_multi_write_node_targets_append(result, (yp_node_t *) target); - } - } else { - if (!token_begins_expression_p(parser->current.type) && !match_type_p(parser, YP_TOKEN_USTAR)) { - if (first_target == NULL && result->targets.size == 0) { - // If we get here, then we weren't able to parse anything at all, so - // we need to return a missing node. - yp_node_destroy(parser, (yp_node_t *) result); - yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "Expected index after for."); - return (yp_node_t *) yp_missing_node_create(parser, operator.start, operator.end); - } - - // If we get here, then we have a trailing , in a multi write node. - // We need to indicate this somehow in the tree, so we'll add an - // anonymous splat. - yp_node_t *splat = (yp_node_t *) yp_splat_node_create(parser, &parser->previous, NULL); - yp_multi_write_node_targets_append(result, splat); - return (yp_node_t *) result; - } - - yp_node_t *target = parse_expression(parser, binding_power, "Expected another expression after ','."); - target = parse_target(parser, target, &operator, NULL); - - yp_multi_write_node_targets_append(result, target); - } - } while (accept(parser, YP_TOKEN_COMMA)); - } - - return (yp_node_t *) result; -} - -// Parse a list of statements separated by newlines or semicolons. -static yp_statements_node_t * -parse_statements(yp_parser_t *parser, yp_context_t context) { - yp_statements_node_t *statements = yp_statements_node_create(parser); - - // First, skip past any optional terminators that might be at the beginning of - // the statements. - while (accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE)); - - // Now, if we have a terminator, then we can just return the empty statements - // node. We should come back in here and make it so that the callers of this - // function can expect a NULL, in which case we wouldn't have to allocate the - // statements at all. - if (context_terminator(context, &parser->current)) return statements; - - // At this point we know we have at least one statement, and that it - // immediately follows the current token. - context_push(parser, context); - - while (true) { - yp_node_t *node = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected to be able to parse an expression."); - yp_statements_node_body_append(statements, node); - - // If we're recovering from a syntax error, then we need to stop parsing the - // statements now. - if (parser->recovering) { - // If this is the level of context where the recovery has happened, then - // we can mark the parser as done recovering. - if (context_terminator(context, &parser->current)) parser->recovering = false; - break; - } - - // If we have a terminator, then we will parse all consequtive terminators - // and then continue parsing the statements list. - if (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { - // If we have a terminator, then we will continue parsing the statements - // list. - while (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)); - if (context_terminator(context, &parser->current)) break; - - // Now we can continue parsing the list of statements. - continue; - } - - // At this point we have a list of statements that are not terminated by a - // newline or semicolon. At this point we need to check if we're at the end - // of the statements list. If we are, then we should break out of the loop. - if (context_terminator(context, &parser->current)) break; - - // At this point, we have a syntax error, because the statement was not - // terminated by a newline or semicolon, and we're not at the end of the - // statements list. Ideally we should scan forward to determine if we should - // insert a missing terminator or break out of parsing the statements list - // at this point. - // - // We don't have that yet, so instead we'll do a more naive approach. If we - // were unable to parse an expression, then we will skip past this token and - // continue parsing the statements list. Otherwise we'll add an error and - // continue parsing the statements list. - if (node->type == YP_NODE_MISSING_NODE) { - parser_lex(parser); - - while (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)); - if (context_terminator(context, &parser->current)) break; - } else { - expect(parser, YP_TOKEN_NEWLINE, "Expected a newline or semicolon after statement."); - } - } - - context_pop(parser); - return statements; -} - -// Parse all of the elements of a hash. -static void -parse_assocs(yp_parser_t *parser, yp_node_t *node) { - assert((node->type == YP_NODE_HASH_NODE) || (node->type == YP_NODE_KEYWORD_HASH_NODE)); - while (true) { - yp_node_t *element; - - switch (parser->current.type) { - case YP_TOKEN_USTAR_STAR: { - parser_lex(parser); - yp_token_t operator = parser->previous; - yp_node_t *value = NULL; - - if (token_begins_expression_p(parser->current.type)) { - value = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected an expression after ** in hash."); - } else if (yp_parser_local_depth(parser, &operator) == -1) { - yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "Expected an expression after ** in hash."); - } - - element = (yp_node_t *) yp_assoc_splat_node_create(parser, value, &operator); - break; - } - case YP_TOKEN_LABEL: { - parser_lex(parser); - - yp_node_t *key = (yp_node_t *) yp_symbol_node_label_create(parser, &parser->previous); - yp_token_t operator = not_provided(parser); - yp_node_t *value = NULL; - - if (token_begins_expression_p(parser->current.type)) { - value = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected an expression after the label in hash."); - } - - element = (yp_node_t *) yp_assoc_node_create(parser, key, &operator, value); - break; - } - default: { - yp_node_t *key = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a key in the hash literal."); - yp_token_t operator; - - if (yp_symbol_node_label_p(key)) { - operator = not_provided(parser); - } else { - expect(parser, YP_TOKEN_EQUAL_GREATER, "Expected a => between the key and the value in the hash."); - operator = parser->previous; - } - - yp_node_t *value = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value in the hash literal."); - element = (yp_node_t *) yp_assoc_node_create(parser, key, &operator, value); - break; - } - } - - if (node->type == YP_NODE_HASH_NODE) { - yp_hash_node_elements_append((yp_hash_node_t *) node, element); - } else { - yp_keyword_hash_node_elements_append((yp_keyword_hash_node_t *) node, element); - } - - // If there's no comma after the element, then we're done. - if (!accept(parser, YP_TOKEN_COMMA)) return; - - // If the next element starts with a label or a **, then we know we have - // another element in the hash, so we'll continue parsing. - if (match_any_type_p(parser, 2, YP_TOKEN_USTAR_STAR, YP_TOKEN_LABEL)) continue; - - // Otherwise we need to check if the subsequent token begins an expression. - // If it does, then we'll continue parsing. - if (token_begins_expression_p(parser->current.type)) continue; - - // Otherwise by default we will exit out of this loop. - return; - } -} - -// Parse a list of arguments. -static void -parse_arguments(yp_parser_t *parser, yp_arguments_node_t *arguments, bool accepts_forwarding, yp_token_type_t terminator) { - yp_binding_power_t binding_power = yp_binding_powers[parser->current.type].left; - - // First we need to check if the next token is one that could be the start of - // an argument. If it's not, then we can just return. - if ( - match_any_type_p(parser, 2, terminator, YP_TOKEN_EOF) || - (binding_power != YP_BINDING_POWER_UNSET && binding_power < YP_BINDING_POWER_RANGE) || - context_terminator(parser->current_context->context, &parser->current) - ) { - return; - } - - bool parsed_bare_hash = false; - bool parsed_block_argument = false; - - while (!match_type_p(parser, YP_TOKEN_EOF)) { - if (parsed_block_argument) { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unexpected argument after block argument."); - } - - yp_node_t *argument = NULL; - - switch (parser->current.type) { - case YP_TOKEN_USTAR_STAR: - case YP_TOKEN_LABEL: { - if (parsed_bare_hash) { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unexpected bare hash."); - } - - yp_keyword_hash_node_t *hash = yp_keyword_hash_node_create(parser); - argument = (yp_node_t *)hash; - - if (!match_any_type_p(parser, 7, terminator, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON, YP_TOKEN_EOF, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_KEYWORD_DO, YP_TOKEN_PARENTHESIS_RIGHT)) { - parse_assocs(parser, (yp_node_t *) hash); - } - - parsed_bare_hash = true; - break; - } - case YP_TOKEN_AMPERSAND: { - parser_lex(parser); - yp_token_t operator = parser->previous; - yp_node_t *expression = NULL; - - if (token_begins_expression_p(parser->current.type)) { - expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected to be able to parse an argument."); - } else if (yp_parser_local_depth(parser, &operator) == -1) { - yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "unexpected & when parent method is not forwarding."); - } - - argument = (yp_node_t *)yp_block_argument_node_create(parser, &operator, expression); - parsed_block_argument = true; - break; - } - case YP_TOKEN_USTAR: { - parser_lex(parser); - yp_token_t operator = parser->previous; - - if (match_any_type_p(parser, 2, YP_TOKEN_PARENTHESIS_RIGHT, YP_TOKEN_COMMA)) { - if (yp_parser_local_depth(parser, &parser->previous) == -1) { - yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "unexpected * when parent method is not forwarding."); - } - - argument = (yp_node_t *) yp_splat_node_create(parser, &operator, NULL); - } else { - yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected an expression after '*' in argument."); - - if (parsed_bare_hash) { - yp_diagnostic_list_append(&parser->error_list, operator.start, expression->location.end, "Unexpected splat argument after double splat."); - } - - argument = (yp_node_t *) yp_splat_node_create(parser, &operator, expression); - } - - break; - } - case YP_TOKEN_UDOT_DOT_DOT: { - if (accepts_forwarding) { - parser_lex(parser); - - if (token_begins_expression_p(parser->current.type)) { - // If the token begins an expression then this ... was not actually - // argument forwarding but was instead a range. - yp_token_t operator = parser->previous; - yp_node_t *right = parse_expression(parser, YP_BINDING_POWER_RANGE, "Expected a value after the operator."); - argument = (yp_node_t *) yp_range_node_create(parser, NULL, &operator, right); - } else { - if (yp_parser_local_depth(parser, &parser->previous) == -1) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "unexpected ... when parent method is not forwarding."); - } - - argument = (yp_node_t *)yp_forwarding_arguments_node_create(parser, &parser->previous); - break; - } - } - } - /* fallthrough */ - default: { - if (argument == NULL) { - argument = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected to be able to parse an argument."); - } - - if (yp_symbol_node_label_p(argument) || accept(parser, YP_TOKEN_EQUAL_GREATER)) { - if (parsed_bare_hash) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected bare hash argument."); - } - - yp_token_t operator; - if (parser->previous.type == YP_TOKEN_EQUAL_GREATER) { - operator = parser->previous; - } else { - operator = not_provided(parser); - } - - yp_keyword_hash_node_t *bare_hash = yp_keyword_hash_node_create(parser); - - // Finish parsing the one we are part way through - yp_node_t *value = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value in the hash literal."); - - argument = (yp_node_t *) yp_assoc_node_create(parser, argument, &operator, value); - yp_keyword_hash_node_elements_append(bare_hash, argument); - argument = (yp_node_t *) bare_hash; - - // Then parse more if we have a comma - if (accept(parser, YP_TOKEN_COMMA) && ( - token_begins_expression_p(parser->current.type) || - match_any_type_p(parser, 2, YP_TOKEN_USTAR_STAR, YP_TOKEN_LABEL) - )) { - parse_assocs(parser, (yp_node_t *) bare_hash); - } - - parsed_bare_hash = true; - } - - break; - } - } - - yp_arguments_node_arguments_append(arguments, argument); - - // If parsing the argument failed, we need to stop parsing arguments. - if (argument->type == YP_NODE_MISSING_NODE || parser->recovering) break; - - // If the terminator of these arguments is not EOF, then we have a specific - // token we're looking for. In that case we can accept a newline here - // because it is not functioning as a statement terminator. - if (terminator != YP_TOKEN_EOF) accept(parser, YP_TOKEN_NEWLINE); - - if (parser->previous.type == YP_TOKEN_COMMA && parsed_bare_hash) { - // If we previously were on a comma and we just parsed a bare hash, then - // we want to continue parsing arguments. This is because the comma was - // grabbed up by the hash parser. - } else { - // If there is no comma at the end of the argument list then we're done - // parsing arguments and can break out of this loop. - if (!accept(parser, YP_TOKEN_COMMA)) break; - } - - // If we hit the terminator, then that means we have a trailing comma so we - // can accept that output as well. - if (match_type_p(parser, terminator)) break; - } -} - -// Required parameters on method, block, and lambda declarations can be -// destructured using parentheses. This looks like: -// -// def foo((bar, baz)) -// end -// -// It can recurse infinitely down, and splats are allowed to group arguments. -static yp_required_destructured_parameter_node_t * -parse_required_destructured_parameter(yp_parser_t *parser) { - expect(parser, YP_TOKEN_PARENTHESIS_LEFT, "Expected '(' to start a required parameter."); - - yp_token_t opening = parser->previous; - yp_required_destructured_parameter_node_t *node = yp_required_destructured_parameter_node_create(parser, &opening); - bool parsed_splat = false; - - do { - yp_node_t *param; - - if (node->parameters.size > 0 && match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { - if (parsed_splat) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected splat after splat."); - } - - param = (yp_node_t *) yp_splat_node_create(parser, &parser->previous, NULL); - yp_required_destructured_parameter_node_append_parameter(node, param); - break; - } - - if (match_type_p(parser, YP_TOKEN_PARENTHESIS_LEFT)) { - param = (yp_node_t *) parse_required_destructured_parameter(parser); - } else if (accept(parser, YP_TOKEN_USTAR)) { - if (parsed_splat) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected splat after splat."); - } - - yp_token_t star = parser->previous; - yp_node_t *value = NULL; - - if (accept(parser, YP_TOKEN_IDENTIFIER)) { - yp_token_t name = parser->previous; - value = (yp_node_t *) yp_required_parameter_node_create(parser, &name); - yp_parser_local_add(parser, &name); - } - - param = (yp_node_t *) yp_splat_node_create(parser, &star, value); - parsed_splat = true; - } else { - expect(parser, YP_TOKEN_IDENTIFIER, "Expected an identifier for a required parameter."); - yp_token_t name = parser->previous; - - param = (yp_node_t *) yp_required_parameter_node_create(parser, &name); - yp_parser_local_add(parser, &name); - } - - yp_required_destructured_parameter_node_append_parameter(node, param); - } while (accept(parser, YP_TOKEN_COMMA)); - - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' to end a required parameter."); - yp_required_destructured_parameter_node_closing_set(node, &parser->previous); - - return node; -} - -// This represents the different order states we can be in when parsing -// method parameters. -typedef enum { - YP_PARAMETERS_NO_CHANGE = 0, // Extra state for tokens that should not change the state - YP_PARAMETERS_ORDER_NOTHING_AFTER = 1, - YP_PARAMETERS_ORDER_KEYWORDS_REST, - YP_PARAMETERS_ORDER_KEYWORDS, - YP_PARAMETERS_ORDER_REST, - YP_PARAMETERS_ORDER_AFTER_OPTIONAL, - YP_PARAMETERS_ORDER_OPTIONAL, - YP_PARAMETERS_ORDER_NAMED, - YP_PARAMETERS_ORDER_NONE, - -} yp_parameters_order_t; - -// This matches parameters tokens with parameters state. -yp_parameters_order_t parameters_ordering[YP_TOKEN_MAXIMUM] = { - [0] = YP_PARAMETERS_NO_CHANGE, - [YP_TOKEN_AMPERSAND] = YP_PARAMETERS_ORDER_NOTHING_AFTER, - [YP_TOKEN_UDOT_DOT_DOT] = YP_PARAMETERS_ORDER_NOTHING_AFTER, - [YP_TOKEN_IDENTIFIER] = YP_PARAMETERS_ORDER_NAMED, - [YP_TOKEN_PARENTHESIS_LEFT] = YP_PARAMETERS_ORDER_NAMED, - [YP_TOKEN_EQUAL] = YP_PARAMETERS_ORDER_OPTIONAL, - [YP_TOKEN_LABEL] = YP_PARAMETERS_ORDER_KEYWORDS, - [YP_TOKEN_USTAR] = YP_PARAMETERS_ORDER_AFTER_OPTIONAL, - [YP_TOKEN_STAR] = YP_PARAMETERS_ORDER_AFTER_OPTIONAL, - [YP_TOKEN_USTAR_STAR] = YP_PARAMETERS_ORDER_KEYWORDS_REST, - [YP_TOKEN_STAR_STAR] = YP_PARAMETERS_ORDER_KEYWORDS_REST -}; - -// Check if current parameter follows valid parameters ordering. If not it adds an -// error to the list without stopping the parsing, otherwise sets the parameters state -// to the one corresponding to the current parameter. -static void -update_parameter_state(yp_parser_t *parser, yp_token_t *token, yp_parameters_order_t *current) { - yp_parameters_order_t state = parameters_ordering[token->type]; - if (state == YP_PARAMETERS_NO_CHANGE) return; - - // If we see another ordered argument after a optional argument - // we only continue parsing ordered arguments until we stop seeing ordered arguments - if (*current == YP_PARAMETERS_ORDER_OPTIONAL && state == YP_PARAMETERS_ORDER_NAMED) { - *current = YP_PARAMETERS_ORDER_AFTER_OPTIONAL; - return; - } else if (*current == YP_PARAMETERS_ORDER_AFTER_OPTIONAL && state == YP_PARAMETERS_ORDER_NAMED) { - return; - } - - if (*current == YP_PARAMETERS_ORDER_NOTHING_AFTER || state > *current) { - // We know what transition we failed on, so we can provide a better error here. - yp_diagnostic_list_append(&parser->error_list, token->start, token->end, "Unexpected parameter order"); - } else if (state < *current) { - *current = state; - } -} - -// Parse a list of parameters on a method definition. -static yp_parameters_node_t * -parse_parameters( - yp_parser_t *parser, - yp_binding_power_t binding_power, - bool uses_parentheses, - bool allows_trailing_comma, - bool allows_forwarding_parameter -) { - yp_parameters_node_t *params = yp_parameters_node_create(parser); - bool looping = true; - - yp_do_loop_stack_push(parser, false); - - yp_parameters_order_t order = YP_PARAMETERS_ORDER_NONE; - - do { - switch (parser->current.type) { - case YP_TOKEN_PARENTHESIS_LEFT: { - update_parameter_state(parser, &parser->current, &order); - yp_node_t *param = (yp_node_t *) parse_required_destructured_parameter(parser); - - if (order > YP_PARAMETERS_ORDER_AFTER_OPTIONAL) { - yp_parameters_node_requireds_append(params, param); - } else { - yp_parameters_node_posts_append(params, param); - } - break; - } - case YP_TOKEN_AMPERSAND: { - update_parameter_state(parser, &parser->current, &order); - parser_lex(parser); - - yp_token_t operator = parser->previous; - yp_token_t name; - - if (accept(parser, YP_TOKEN_IDENTIFIER)) { - name = parser->previous; - yp_parser_parameter_name_check(parser, &name); - yp_parser_local_add(parser, &name); - } else { - name = not_provided(parser); - yp_parser_local_add(parser, &operator); - } - - yp_block_parameter_node_t *param = yp_block_parameter_node_create(parser, &name, &operator); - yp_parameters_node_block_set(params, param); - break; - } - case YP_TOKEN_UDOT_DOT_DOT: { - if (!allows_forwarding_parameter) { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unexpected ..."); - } - if (order > YP_PARAMETERS_ORDER_NOTHING_AFTER) { - update_parameter_state(parser, &parser->current, &order); - parser_lex(parser); - - yp_parser_local_add(parser, &parser->previous); - yp_forwarding_parameter_node_t *param = yp_forwarding_parameter_node_create(parser, &parser->previous); - yp_parameters_node_keyword_rest_set(params, (yp_node_t *)param); - } else { - update_parameter_state(parser, &parser->current, &order); - parser_lex(parser); - } - break; - } - case YP_TOKEN_IDENTIFIER: { - parser_lex(parser); - - if (parser->current.type == YP_TOKEN_EQUAL) { - update_parameter_state(parser, &parser->current, &order); - } else { - update_parameter_state(parser, &parser->previous, &order); - } - - yp_token_t name = parser->previous; - yp_parser_parameter_name_check(parser, &name); - yp_parser_local_add(parser, &name); - - if (accept(parser, YP_TOKEN_EQUAL)) { - yp_token_t operator = parser->previous; - context_push(parser, YP_CONTEXT_DEFAULT_PARAMS); - yp_node_t *value = parse_expression(parser, binding_power, "Expected to find a default value for the parameter."); - - yp_optional_parameter_node_t *param = yp_optional_parameter_node_create(parser, &name, &operator, value); - yp_parameters_node_optionals_append(params, param); - context_pop(parser); - - // If parsing the value of the parameter resulted in error recovery, - // then we can put a missing node in its place and stop parsing the - // parameters entirely now. - if (parser->recovering) { - looping = false; - break; - } - } else if (order > YP_PARAMETERS_ORDER_AFTER_OPTIONAL) { - yp_required_parameter_node_t *param = yp_required_parameter_node_create(parser, &name); - yp_parameters_node_requireds_append(params, (yp_node_t *) param); - } else { - yp_required_parameter_node_t *param = yp_required_parameter_node_create(parser, &name); - yp_parameters_node_posts_append(params, (yp_node_t *) param); - } - - break; - } - case YP_TOKEN_LABEL: { - if (!uses_parentheses) parser->in_keyword_arg = true; - update_parameter_state(parser, &parser->current, &order); - parser_lex(parser); - - yp_token_t name = parser->previous; - yp_token_t local = name; - local.end -= 1; - - yp_parser_parameter_name_check(parser, &local); - yp_parser_local_add(parser, &local); - - switch (parser->current.type) { - case YP_TOKEN_COMMA: - case YP_TOKEN_PARENTHESIS_RIGHT: - case YP_TOKEN_PIPE: { - yp_node_t *param = (yp_node_t *) yp_keyword_parameter_node_create(parser, &name, NULL); - yp_parameters_node_keywords_append(params, param); - break; - } - case YP_TOKEN_SEMICOLON: - case YP_TOKEN_NEWLINE: { - if (uses_parentheses) { - looping = false; - break; - } - - yp_node_t *param = (yp_node_t *) yp_keyword_parameter_node_create(parser, &name, NULL); - yp_parameters_node_keywords_append(params, param); - break; - } - default: { - yp_node_t *value = NULL; - if (token_begins_expression_p(parser->current.type)) { - context_push(parser, YP_CONTEXT_DEFAULT_PARAMS); - value = parse_expression(parser, binding_power, "Expected to find a default value for the keyword parameter."); - context_pop(parser); - } - - yp_node_t *param = (yp_node_t *) yp_keyword_parameter_node_create(parser, &name, value); - yp_parameters_node_keywords_append(params, param); - - // If parsing the value of the parameter resulted in error recovery, - // then we can put a missing node in its place and stop parsing the - // parameters entirely now. - if (parser->recovering) { - looping = false; - break; - } - } - } - - parser->in_keyword_arg = false; - break; - } - case YP_TOKEN_USTAR: - case YP_TOKEN_STAR: { - update_parameter_state(parser, &parser->current, &order); - parser_lex(parser); - - yp_token_t operator = parser->previous; - yp_token_t name; - - if (accept(parser, YP_TOKEN_IDENTIFIER)) { - name = parser->previous; - yp_parser_parameter_name_check(parser, &name); - yp_parser_local_add(parser, &name); - } else { - name = not_provided(parser); - yp_parser_local_add(parser, &operator); - } - - yp_rest_parameter_node_t *param = yp_rest_parameter_node_create(parser, &operator, &name); - yp_parameters_node_rest_set(params, param); - break; - } - case YP_TOKEN_STAR_STAR: - case YP_TOKEN_USTAR_STAR: { - update_parameter_state(parser, &parser->current, &order); - parser_lex(parser); - - yp_token_t operator = parser->previous; - yp_node_t *param; - - if (accept(parser, YP_TOKEN_KEYWORD_NIL)) { - param = (yp_node_t *) yp_no_keywords_parameter_node_create(parser, &operator, &parser->previous); - } else { - yp_token_t name; - - if (accept(parser, YP_TOKEN_IDENTIFIER)) { - name = parser->previous; - yp_parser_parameter_name_check(parser, &name); - yp_parser_local_add(parser, &name); - } else { - name = not_provided(parser); - yp_parser_local_add(parser, &operator); - } - - param = (yp_node_t *) yp_keyword_rest_parameter_node_create(parser, &operator, &name); - } - - yp_parameters_node_keyword_rest_set(params, param); - break; - } - case YP_TOKEN_CONSTANT: - parser_lex(parser); - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Formal argument cannot be a constant"); - break; - case YP_TOKEN_INSTANCE_VARIABLE: - parser_lex(parser); - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Formal argument cannot be an instance variable"); - break; - case YP_TOKEN_GLOBAL_VARIABLE: - parser_lex(parser); - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Formal argument cannot be a global variable"); - break; - case YP_TOKEN_CLASS_VARIABLE: - parser_lex(parser); - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Formal argument cannot be a class variable"); - break; - default: - if (parser->previous.type == YP_TOKEN_COMMA) { - if (allows_trailing_comma) { - // If we get here, then we have a trailing comma in a block - // parameter list. We need to create an anonymous rest parameter to - // represent it. - yp_token_t name = not_provided(parser); - yp_rest_parameter_node_t *param = yp_rest_parameter_node_create(parser, &parser->previous, &name); - yp_parameters_node_rest_set(params, param); - } else { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected ','."); - } - } - - looping = false; - break; - } - - if (looping && uses_parentheses) { - accept(parser, YP_TOKEN_NEWLINE); - } - } while (looping && accept(parser, YP_TOKEN_COMMA)); - - yp_do_loop_stack_pop(parser); - return params; -} - -// Parse any number of rescue clauses. This will form a linked list of if -// nodes pointing to each other from the top. -static inline void -parse_rescues(yp_parser_t *parser, yp_begin_node_t *parent_node) { - yp_rescue_node_t *current = NULL; - - while (accept(parser, YP_TOKEN_KEYWORD_RESCUE)) { - yp_rescue_node_t *rescue = yp_rescue_node_create(parser, &parser->previous); - - switch (parser->current.type) { - case YP_TOKEN_EQUAL_GREATER: { - // Here we have an immediate => after the rescue keyword, in which case - // we're going to have an empty list of exceptions to rescue (which - // implies StandardError). - parser_lex(parser); - rescue->equal_greater = parser->previous; - - yp_node_t *node = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected an exception variable after `=>` in rescue statement."); - yp_token_t operator = not_provided(parser); - node = parse_target(parser, node, &operator, NULL); - - rescue->exception = node; - break; - } - case YP_TOKEN_NEWLINE: - case YP_TOKEN_SEMICOLON: - case YP_TOKEN_KEYWORD_THEN: - // Here we have a terminator for the rescue keyword, in which case we're - // going to just continue on. - break; - default: { - if (token_begins_expression_p(parser->current.type) || match_type_p(parser, YP_TOKEN_USTAR)) { - // Here we have something that could be an exception expression, so - // we'll attempt to parse it here and any others delimited by commas. - - do { - yp_node_t *expression = parse_starred_expression(parser, YP_BINDING_POWER_DEFINED, "Expected to find a rescued expression."); - yp_rescue_node_exceptions_append(rescue, expression); - - // If we hit a newline, then this is the end of the rescue expression. We - // can continue on to parse the statements. - if (match_any_type_p(parser, 3, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON, YP_TOKEN_KEYWORD_THEN)) break; - - // If we hit a `=>` then we're going to parse the exception variable. Once - // we've done that, we'll break out of the loop and parse the statements. - if (accept(parser, YP_TOKEN_EQUAL_GREATER)) { - rescue->equal_greater = parser->previous; - - yp_node_t *node = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected an exception variable after `=>` in rescue statement."); - yp_token_t operator = not_provided(parser); - node = parse_target(parser, node, &operator, NULL); - - yp_rescue_node_exception_set(rescue, node); - break; - } - } while (accept(parser, YP_TOKEN_COMMA)); - } - } - } - - if (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { - accept(parser, YP_TOKEN_KEYWORD_THEN); - } else { - expect(parser, YP_TOKEN_KEYWORD_THEN, "Expected a terminator after rescue clause."); - } - - if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { - yp_rescue_node_statements_set(rescue, parse_statements(parser, YP_CONTEXT_RESCUE)); - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - } - - if (current == NULL) { - yp_begin_node_rescue_clause_set(parent_node, rescue); - } else { - yp_rescue_node_consequent_set(current, rescue); - } - - current = rescue; - } - - if (accept(parser, YP_TOKEN_KEYWORD_ELSE)) { - yp_token_t else_keyword = parser->previous; - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - - yp_statements_node_t *else_statements = NULL; - if (!match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_END, YP_TOKEN_KEYWORD_ENSURE)) { - else_statements = parse_statements(parser, YP_CONTEXT_RESCUE_ELSE); - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - } - - yp_else_node_t *else_clause = yp_else_node_create(parser, &else_keyword, else_statements, &parser->current); - yp_begin_node_else_clause_set(parent_node, else_clause); - } - - if (accept(parser, YP_TOKEN_KEYWORD_ENSURE)) { - yp_token_t ensure_keyword = parser->previous; - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - - yp_statements_node_t *ensure_statements = NULL; - if (!match_type_p(parser, YP_TOKEN_KEYWORD_END)) { - ensure_statements = parse_statements(parser, YP_CONTEXT_ENSURE); - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - } - - yp_ensure_node_t *ensure_clause = yp_ensure_node_create(parser, &ensure_keyword, ensure_statements, &parser->current); - yp_begin_node_ensure_clause_set(parent_node, ensure_clause); - } - - if (parser->current.type == YP_TOKEN_KEYWORD_END) { - yp_begin_node_end_keyword_set(parent_node, &parser->current); - } else { - yp_token_t end_keyword = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; - yp_begin_node_end_keyword_set(parent_node, &end_keyword); - } -} - -static inline yp_begin_node_t * -parse_rescues_as_begin(yp_parser_t *parser, yp_statements_node_t *statements) { - yp_token_t no_begin_token = not_provided(parser); - yp_begin_node_t *begin_node = yp_begin_node_create(parser, &no_begin_token, statements); - parse_rescues(parser, begin_node); - return begin_node; -} - -// Parse a list of parameters and local on a block definition. -static yp_block_parameters_node_t * -parse_block_parameters( - yp_parser_t *parser, - bool allows_trailing_comma, - const yp_token_t *opening, - bool is_lambda_literal -) { - yp_parameters_node_t *parameters = NULL; - if (!match_type_p(parser, YP_TOKEN_SEMICOLON)) { - parameters = parse_parameters( - parser, - is_lambda_literal ? YP_BINDING_POWER_DEFINED : YP_BINDING_POWER_INDEX, - false, - allows_trailing_comma, - false - ); - } - - yp_block_parameters_node_t *block_parameters = yp_block_parameters_node_create(parser, parameters, opening); - if (accept(parser, YP_TOKEN_SEMICOLON)) { - do { - expect(parser, YP_TOKEN_IDENTIFIER, "Expected a local variable name."); - yp_parser_local_add(parser, &parser->previous); - yp_block_parameters_node_append_local(block_parameters, &parser->previous); - } while (accept(parser, YP_TOKEN_COMMA)); - } - - return block_parameters; -} - -// Parse a block. -static yp_block_node_t * -parse_block(yp_parser_t *parser) { - yp_token_t opening = parser->previous; - accept(parser, YP_TOKEN_NEWLINE); - - yp_accepts_block_stack_push(parser, true); - yp_parser_scope_push(parser, false); - yp_block_parameters_node_t *parameters = NULL; - - if (accept(parser, YP_TOKEN_PIPE)) { - yp_token_t block_parameters_opening = parser->previous; - - if (match_type_p(parser, YP_TOKEN_PIPE)) { - parameters = yp_block_parameters_node_create(parser, NULL, &block_parameters_opening); - parser->command_start = true; - parser_lex(parser); - } else { - parameters = parse_block_parameters(parser, true, &block_parameters_opening, false); - accept(parser, YP_TOKEN_NEWLINE); - parser->command_start = true; - expect(parser, YP_TOKEN_PIPE, "Expected block parameters to end with '|'."); - } - - yp_block_parameters_node_closing_set(parameters, &parser->previous); - } - - accept(parser, YP_TOKEN_NEWLINE); - yp_node_t *statements = NULL; - - if (opening.type == YP_TOKEN_BRACE_LEFT) { - if (!match_type_p(parser, YP_TOKEN_BRACE_RIGHT)) { - statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_BLOCK_BRACES); - } - - expect(parser, YP_TOKEN_BRACE_RIGHT, "Expected block beginning with '{' to end with '}'."); - } else { - if (!match_type_p(parser, YP_TOKEN_KEYWORD_END)) { - if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_KEYWORD_ENSURE)) { - statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_BLOCK_KEYWORDS); - } - - if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { - assert(statements == NULL || statements->type == YP_NODE_STATEMENTS_NODE); - statements = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) statements); - } - } - - expect(parser, YP_TOKEN_KEYWORD_END, "Expected block beginning with 'do' to end with 'end'."); - } - - yp_token_list_t locals = parser->current_scope->locals; - yp_parser_scope_pop(parser); - yp_accepts_block_stack_pop(parser); - return yp_block_node_create(parser, &locals, &opening, parameters, statements, &parser->previous); -} - -// Parse a list of arguments and their surrounding parentheses if they are -// present. -static void -parse_arguments_list(yp_parser_t *parser, yp_arguments_t *arguments, bool accepts_block) { - if (accept(parser, YP_TOKEN_PARENTHESIS_LEFT)) { - arguments->opening = parser->previous; - - if (accept(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { - arguments->closing = parser->previous; - } else { - arguments->arguments = yp_arguments_node_create(parser); - - yp_accepts_block_stack_push(parser, true); - parse_arguments(parser, arguments->arguments, true, YP_TOKEN_PARENTHESIS_RIGHT); - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a ')' to close the argument list."); - yp_accepts_block_stack_pop(parser); - - arguments->closing = parser->previous; - } - } else if ((token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR)) && !match_type_p(parser, YP_TOKEN_BRACE_LEFT)) { - yp_accepts_block_stack_push(parser, false); - - // If we get here, then the subsequent token cannot be used as an infix - // operator. In this case we assume the subsequent token is part of an - // argument to this method call. - arguments->arguments = yp_arguments_node_create(parser); - parse_arguments(parser, arguments->arguments, true, YP_TOKEN_EOF); - - yp_accepts_block_stack_pop(parser); - } - - // If we're at the end of the arguments, we can now check if there is a block - // node that starts with a {. If there is, then we can parse it and add it to - // the arguments. - if (accepts_block) { - if (accept(parser, YP_TOKEN_BRACE_LEFT)) { - arguments->block = parse_block(parser); - } else if (yp_accepts_block_stack_p(parser) && accept(parser, YP_TOKEN_KEYWORD_DO)) { - arguments->block = parse_block(parser); - } - } -} - -static inline yp_node_t * -parse_conditional(yp_parser_t *parser, yp_context_t context) { - yp_token_t keyword = parser->previous; - - context_push(parser, YP_CONTEXT_PREDICATE); - yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_MODIFIER, "Expected to find a predicate for the conditional."); - - // Predicates are closed by a term, a "then", or a term and then a "then". - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - accept(parser, YP_TOKEN_KEYWORD_THEN); - - context_pop(parser); - yp_statements_node_t *statements = NULL; - - if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_ELSIF, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_KEYWORD_END)) { - yp_accepts_block_stack_push(parser, true); - statements = parse_statements(parser, context); - yp_accepts_block_stack_pop(parser); - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - } - - yp_token_t end_keyword = not_provided(parser); - yp_node_t *parent; - - switch (context) { - case YP_CONTEXT_IF: - parent = (yp_node_t *) yp_if_node_create(parser, &keyword, predicate, statements, NULL, &end_keyword); - break; - case YP_CONTEXT_UNLESS: - parent = (yp_node_t *) yp_unless_node_create(parser, &keyword, predicate, statements); - break; - default: - parent = NULL; - assert(false && "unreachable"); - break; - } - - yp_node_t *current = parent; - - // Parse any number of elsif clauses. This will form a linked list of if - // nodes pointing to each other from the top. - if (context == YP_CONTEXT_IF) { - while (accept(parser, YP_TOKEN_KEYWORD_ELSIF)) { - yp_token_t elsif_keyword = parser->previous; - yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_MODIFIER, "Expected to find a predicate for the elsif clause."); - - // Predicates are closed by a term, a "then", or a term and then a "then". - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - accept(parser, YP_TOKEN_KEYWORD_THEN); - - yp_statements_node_t *statements = parse_statements(parser, YP_CONTEXT_ELSIF); - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - - yp_node_t *elsif = (yp_node_t *) yp_if_node_create(parser, &elsif_keyword, predicate, statements, NULL, &end_keyword); - ((yp_if_node_t *) current)->consequent = elsif; - current = elsif; - } - } - - if (match_type_p(parser, YP_TOKEN_KEYWORD_ELSE)) { - parser_lex(parser); - yp_token_t else_keyword = parser->previous; - yp_statements_node_t *else_statements = parse_statements(parser, YP_CONTEXT_ELSE); - - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `else` clause."); - - yp_else_node_t *else_node = yp_else_node_create(parser, &else_keyword, else_statements, &parser->previous); - - switch (context) { - case YP_CONTEXT_IF: - ((yp_if_node_t *) current)->consequent = (yp_node_t *) else_node; - ((yp_if_node_t *) parent)->end_keyword = parser->previous; - break; - case YP_CONTEXT_UNLESS: - ((yp_unless_node_t *) parent)->consequent = else_node; - ((yp_unless_node_t *) parent)->end_keyword = parser->previous; - break; - default: - assert(false && "unreachable"); - break; - } - } else { - expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `if` statement."); - - switch (context) { - case YP_CONTEXT_IF: - ((yp_if_node_t *) parent)->end_keyword = parser->previous; - parent->location.end = parser->previous.end; - break; - case YP_CONTEXT_UNLESS: - ((yp_unless_node_t *) parent)->end_keyword = parser->previous; - parent->location.end = parser->previous.end; - break; - default: - assert(false && "unreachable"); - break; - } - } - - return parent; -} - -// This macro allows you to define a case statement for all of the keywords. -// It's meant to be used in a switch statement. -#define YP_CASE_KEYWORD YP_TOKEN_KEYWORD___ENCODING__: case YP_TOKEN_KEYWORD___FILE__: case YP_TOKEN_KEYWORD___LINE__: \ - case YP_TOKEN_KEYWORD_ALIAS: case YP_TOKEN_KEYWORD_AND: case YP_TOKEN_KEYWORD_BEGIN: case YP_TOKEN_KEYWORD_BEGIN_UPCASE: \ - case YP_TOKEN_KEYWORD_BREAK: case YP_TOKEN_KEYWORD_CASE: case YP_TOKEN_KEYWORD_CLASS: case YP_TOKEN_KEYWORD_DEF: \ - case YP_TOKEN_KEYWORD_DEFINED: case YP_TOKEN_KEYWORD_DO: case YP_TOKEN_KEYWORD_DO_LOOP: case YP_TOKEN_KEYWORD_ELSE: \ - case YP_TOKEN_KEYWORD_ELSIF: case YP_TOKEN_KEYWORD_END: case YP_TOKEN_KEYWORD_END_UPCASE: case YP_TOKEN_KEYWORD_ENSURE: \ - case YP_TOKEN_KEYWORD_FALSE: case YP_TOKEN_KEYWORD_FOR: case YP_TOKEN_KEYWORD_IF: case YP_TOKEN_KEYWORD_IN: \ - case YP_TOKEN_KEYWORD_MODULE: case YP_TOKEN_KEYWORD_NEXT: case YP_TOKEN_KEYWORD_NIL: case YP_TOKEN_KEYWORD_NOT: \ - case YP_TOKEN_KEYWORD_OR: case YP_TOKEN_KEYWORD_REDO: case YP_TOKEN_KEYWORD_RESCUE: case YP_TOKEN_KEYWORD_RETRY: \ - case YP_TOKEN_KEYWORD_RETURN: case YP_TOKEN_KEYWORD_SELF: case YP_TOKEN_KEYWORD_SUPER: case YP_TOKEN_KEYWORD_THEN: \ - case YP_TOKEN_KEYWORD_TRUE: case YP_TOKEN_KEYWORD_UNDEF: case YP_TOKEN_KEYWORD_UNLESS: case YP_TOKEN_KEYWORD_UNTIL: \ - case YP_TOKEN_KEYWORD_WHEN: case YP_TOKEN_KEYWORD_WHILE: case YP_TOKEN_KEYWORD_YIELD - - -// This macro allows you to define a case statement for all of the operators. -// It's meant to be used in a switch statement. -#define YP_CASE_OPERATOR YP_TOKEN_AMPERSAND: case YP_TOKEN_BACKTICK: case YP_TOKEN_BANG_EQUAL: \ - case YP_TOKEN_BANG_TILDE: case YP_TOKEN_BANG: case YP_TOKEN_BRACKET_LEFT_RIGHT_EQUAL: \ - case YP_TOKEN_BRACKET_LEFT_RIGHT: case YP_TOKEN_CARET: case YP_TOKEN_EQUAL_EQUAL_EQUAL: case YP_TOKEN_EQUAL_EQUAL: \ - case YP_TOKEN_EQUAL_TILDE: case YP_TOKEN_GREATER_EQUAL: case YP_TOKEN_GREATER_GREATER: case YP_TOKEN_GREATER: \ - case YP_TOKEN_LESS_EQUAL_GREATER: case YP_TOKEN_LESS_EQUAL: case YP_TOKEN_LESS_LESS: case YP_TOKEN_LESS: \ - case YP_TOKEN_MINUS: case YP_TOKEN_PERCENT: case YP_TOKEN_PIPE: case YP_TOKEN_PLUS: case YP_TOKEN_SLASH: \ - case YP_TOKEN_STAR_STAR: case YP_TOKEN_STAR: case YP_TOKEN_TILDE: case YP_TOKEN_UMINUS: case YP_TOKEN_UMINUS_NUM: \ - case YP_TOKEN_UPLUS: case YP_TOKEN_USTAR: case YP_TOKEN_USTAR_STAR - -// This macro allows you to define a case statement for all of the token types -// that represent the beginning of nodes that are "primitives" in a pattern -// matching expression. -#define YP_CASE_PRIMITIVE YP_TOKEN_INTEGER: case YP_TOKEN_FLOAT: case YP_TOKEN_RATIONAL_NUMBER: \ - case YP_TOKEN_IMAGINARY_NUMBER: case YP_TOKEN_SYMBOL_BEGIN: case YP_TOKEN_REGEXP_BEGIN: case YP_TOKEN_BACKTICK: \ - case YP_TOKEN_PERCENT_LOWER_X: case YP_TOKEN_PERCENT_LOWER_I: case YP_TOKEN_PERCENT_LOWER_W: \ - case YP_TOKEN_PERCENT_UPPER_I: case YP_TOKEN_PERCENT_UPPER_W: case YP_TOKEN_STRING_BEGIN: case YP_TOKEN_KEYWORD_NIL: \ - case YP_TOKEN_KEYWORD_SELF: case YP_TOKEN_KEYWORD_TRUE: case YP_TOKEN_KEYWORD_FALSE: case YP_TOKEN_KEYWORD___FILE__: \ - case YP_TOKEN_KEYWORD___LINE__: case YP_TOKEN_KEYWORD___ENCODING__: case YP_TOKEN_MINUS_GREATER: \ - case YP_TOKEN_HEREDOC_START: case YP_TOKEN_UMINUS_NUM - -// This macro allows you to define a case statement for all of the token types -// that could begin a parameter. -#define YP_CASE_PARAMETER YP_TOKEN_AMPERSAND: case YP_TOKEN_UDOT_DOT_DOT: case YP_TOKEN_IDENTIFIER: \ - case YP_TOKEN_LABEL: case YP_TOKEN_USTAR: case YP_TOKEN_STAR: case YP_TOKEN_STAR_STAR: case YP_TOKEN_USTAR_STAR: case YP_TOKEN_CONSTANT: \ - case YP_TOKEN_INSTANCE_VARIABLE: case YP_TOKEN_GLOBAL_VARIABLE: case YP_TOKEN_CLASS_VARIABLE - -// This macro allows you to define a case statement for all of the nodes that -// can be transformed into write targets. -#define YP_CASE_WRITABLE YP_NODE_CLASS_VARIABLE_READ_NODE: case YP_NODE_CONSTANT_PATH_NODE: \ - case YP_NODE_CONSTANT_READ_NODE: case YP_NODE_GLOBAL_VARIABLE_READ_NODE: case YP_NODE_LOCAL_VARIABLE_READ_NODE: \ - case YP_NODE_INSTANCE_VARIABLE_READ_NODE: case YP_NODE_MULTI_WRITE_NODE - -// Parse a node that is part of a string. If the subsequent tokens cannot be -// parsed as a string part, then NULL is returned. -static yp_node_t * -parse_string_part(yp_parser_t *parser) { - switch (parser->current.type) { - // Here the lexer has returned to us plain string content. In this case - // we'll create a string node that has no opening or closing and return that - // as the part. These kinds of parts look like: - // - // "aaa #{bbb} #@ccc ddd" - // ^^^^ ^ ^^^^ - case YP_TOKEN_STRING_CONTENT: { - yp_unescape_type_t unescape_type = YP_UNESCAPE_ALL; - - if (parser->lex_modes.current->mode == YP_LEX_HEREDOC) { - if (parser->lex_modes.current->as.heredoc.quote == YP_HEREDOC_QUOTE_SINGLE) { - unescape_type = YP_UNESCAPE_MINIMAL; - } - } - - parser_lex(parser); - - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - - return (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &parser->previous, &closing, unescape_type); - } - // Here the lexer has returned the beginning of an embedded expression. In - // that case we'll parse the inner statements and return that as the part. - // These kinds of parts look like: - // - // "aaa #{bbb} #@ccc ddd" - // ^^^^^^ - case YP_TOKEN_EMBEXPR_BEGIN: { - yp_lex_state_t state = parser->lex_state; - int brace_nesting = parser->brace_nesting; - - parser->brace_nesting = 0; - lex_state_set(parser, YP_LEX_STATE_BEG); - parser_lex(parser); - - yp_token_t opening = parser->previous; - yp_statements_node_t *statements = NULL; - - if (!match_type_p(parser, YP_TOKEN_EMBEXPR_END)) { - yp_accepts_block_stack_push(parser, true); - statements = parse_statements(parser, YP_CONTEXT_EMBEXPR); - yp_accepts_block_stack_pop(parser); - } - - parser->brace_nesting = brace_nesting; - lex_state_set(parser, state); - - expect(parser, YP_TOKEN_EMBEXPR_END, "Expected a closing delimiter for an embedded expression."); - yp_token_t closing = parser->previous; - - return (yp_node_t *) yp_string_interpolated_node_create(parser, &opening, statements, &closing); - } - // Here the lexer has returned the beginning of an embedded variable. In - // that case we'll parse the variable and create an appropriate node for it - // and then return that node. These kinds of parts look like: - // - // "aaa #{bbb} #@ccc ddd" - // ^^^^^ - case YP_TOKEN_EMBVAR: { - lex_state_set(parser, YP_LEX_STATE_BEG); - parser_lex(parser); - - switch (parser->current.type) { - // In this case a global variable is being interpolated. We'll create - // a global variable read node. - case YP_TOKEN_BACK_REFERENCE: - case YP_TOKEN_GLOBAL_VARIABLE: - case YP_TOKEN_NTH_REFERENCE: - parser_lex(parser); - return (yp_node_t *) yp_global_variable_read_node_create(parser, &parser->previous); - // In this case an instance variable is being interpolated. We'll - // create an instance variable read node. - case YP_TOKEN_INSTANCE_VARIABLE: - parser_lex(parser); - return (yp_node_t *) yp_instance_variable_read_node_create(parser, &parser->previous); - // In this case a class variable is being interpolated. We'll create a - // class variable read node. - case YP_TOKEN_CLASS_VARIABLE: - parser_lex(parser); - return (yp_node_t *) yp_class_variable_read_node_create(parser, &parser->previous); - // We can hit here if we got an invalid token. In that case we'll not - // attempt to lex this token and instead just return a missing node. - default: - expect(parser, YP_TOKEN_IDENTIFIER, "Expected a valid embedded variable."); - return (yp_node_t *) yp_missing_node_create(parser, parser->current.start, parser->current.end); - } - } - default: - parser_lex(parser); - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Could not understand string part"); - return NULL; - } -} - -static yp_node_t * -parse_symbol(yp_parser_t *parser, yp_lex_mode_t *lex_mode, yp_lex_state_t next_state) { - yp_token_t opening = parser->previous; - - if (lex_mode->mode != YP_LEX_STRING) { - if (next_state != YP_LEX_STATE_NONE) { - lex_state_set(parser, next_state); - } - yp_token_t symbol; - - switch (parser->current.type) { - case YP_TOKEN_IDENTIFIER: - case YP_TOKEN_CONSTANT: - case YP_TOKEN_INSTANCE_VARIABLE: - case YP_TOKEN_CLASS_VARIABLE: - case YP_TOKEN_GLOBAL_VARIABLE: - case YP_TOKEN_NTH_REFERENCE: - case YP_TOKEN_BACK_REFERENCE: - case YP_CASE_KEYWORD: - parser_lex(parser); - symbol = parser->previous; - break; - case YP_CASE_OPERATOR: - lex_state_set(parser, next_state == YP_LEX_STATE_NONE ? YP_LEX_STATE_ENDFN : next_state); - parser_lex(parser); - symbol = parser->previous; - break; - default: - expect(parser, YP_TOKEN_IDENTIFIER, "Expected symbol."); - symbol = parser->previous; - break; - } - - yp_token_t closing = not_provided(parser); - return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &symbol, &closing); - } - - // If we weren't in a string in the previous check then we have to be now. - assert(lex_mode->mode == YP_LEX_STRING); - - if (lex_mode->as.string.interpolation) { - yp_interpolated_symbol_node_t *interpolated = yp_interpolated_symbol_node_create(parser, &opening, NULL, &opening); - - while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { - yp_node_t *part = parse_string_part(parser); - if (part != NULL) { - yp_interpolated_symbol_node_append(interpolated, part); - } - } - - if (next_state != YP_LEX_STATE_NONE) { - lex_state_set(parser, next_state); - } - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for an interpolated symbol."); - - yp_interpolated_symbol_node_closing_set(interpolated, &parser->previous); - return (yp_node_t *) interpolated; - } - - yp_token_t content; - if (accept(parser, YP_TOKEN_STRING_CONTENT)) { - content = parser->previous; - } else { - content = (yp_token_t) { .type = YP_TOKEN_STRING_CONTENT, .start = parser->previous.end, .end = parser->previous.end }; - } - - if (next_state != YP_LEX_STATE_NONE) { - lex_state_set(parser, next_state); - } - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a dynamic symbol."); - - return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &content, &parser->previous); -} - -// Parse an argument to undef which can either be a bare word, a -// symbol, a constant, or an interpolated symbol. -static inline yp_node_t * -parse_undef_argument(yp_parser_t *parser) { - switch (parser->current.type) { - case YP_CASE_KEYWORD: - case YP_CASE_OPERATOR: - case YP_TOKEN_CONSTANT: - case YP_TOKEN_IDENTIFIER: { - parser_lex(parser); - - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - - return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &parser->previous, &closing); - } - case YP_TOKEN_SYMBOL_BEGIN: { - yp_lex_mode_t *lex_mode = parser->lex_modes.current; - parser_lex(parser); - return parse_symbol(parser, lex_mode, YP_LEX_STATE_NONE); - } - default: - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Expected a bare word or symbol argument."); - return (yp_node_t *) yp_missing_node_create(parser, parser->current.start, parser->current.end); - } -} - -// Parse an argument to alias which can either be a bare word, a symbol, an -// interpolated symbol or a global variable. If this is the first argument, then -// we need to set the lex state to YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM -// between the first and second arguments. -static inline yp_node_t * -parse_alias_argument(yp_parser_t *parser, bool first) { - switch (parser->current.type) { - case YP_CASE_OPERATOR: - case YP_CASE_KEYWORD: - case YP_TOKEN_CONSTANT: - case YP_TOKEN_IDENTIFIER: { - if (first) { - lex_state_set(parser, YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM); - } - - parser_lex(parser); - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - - return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &parser->previous, &closing); - } - case YP_TOKEN_SYMBOL_BEGIN: { - yp_lex_mode_t *lex_mode = parser->lex_modes.current; - parser_lex(parser); - - return parse_symbol(parser, lex_mode, first ? YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM : YP_LEX_STATE_NONE); - } - case YP_TOKEN_BACK_REFERENCE: - case YP_TOKEN_NTH_REFERENCE: - case YP_TOKEN_GLOBAL_VARIABLE: { - parser_lex(parser); - return (yp_node_t *) yp_global_variable_read_node_create(parser, &parser->previous); - } - default: - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Expected a bare word, symbol or global variable argument."); - return (yp_node_t *) yp_missing_node_create(parser, parser->current.start, parser->current.end); - } -} - -// Parse an identifier into either a local variable read or a call. -static yp_node_t * -parse_vcall(yp_parser_t *parser) { - int depth; - - if ( - (parser->current.type != YP_TOKEN_PARENTHESIS_LEFT) && - (parser->previous.end[-1] != '!') && - (parser->previous.end[-1] != '?') && - (depth = yp_parser_local_depth(parser, &parser->previous)) != -1 - ) { - return (yp_node_t *) yp_local_variable_read_node_create(parser, &parser->previous, (uint32_t) depth); - } - - return (yp_node_t *) yp_call_node_vcall_create(parser, &parser->previous); -} - -static inline yp_token_t -parse_method_definition_name(yp_parser_t *parser) { - switch (parser->current.type) { - case YP_CASE_KEYWORD: - case YP_TOKEN_CONSTANT: - case YP_TOKEN_IDENTIFIER: - parser_lex(parser); - return parser->previous; - case YP_CASE_OPERATOR: - lex_state_set(parser, YP_LEX_STATE_ENDFN); - parser_lex(parser); - return parser->previous; - default: - return not_provided(parser); - } -} - -// Calculate the common leading whitespace for each line in a heredoc. -static int -parse_heredoc_common_whitespace(yp_parser_t *parser, yp_node_list_t *nodes) { - int common_whitespace = -1; - - for (size_t index = 0; index < nodes->size; index++) { - yp_node_t *node = nodes->nodes[index]; - - if (node->type != YP_NODE_STRING_NODE) continue; - yp_token_t *content = &((yp_string_node_t *) node)->content; - - // If the previous node wasn't a string node, we don't want to trim - // whitespace. This could happen after an interpolated expression or - // variable. - if (index == 0 || nodes->nodes[index - 1]->type == YP_NODE_STRING_NODE) { - int cur_whitespace; - const char *cur_char = content->start; - - while (cur_char && cur_char < content->end) { - // Any empty newlines aren't included in the minimum whitespace calculation - while (cur_char < content->end && *cur_char == '\n') cur_char++; - while (cur_char + 1 < content->end && *cur_char == '\r' && cur_char[1] == '\n') cur_char += 2; - - if (cur_char == content->end) break; - - cur_whitespace = 0; - - while (yp_char_is_inline_whitespace(*cur_char) && cur_char < content->end) { - if (cur_char[0] == '\t') { - cur_whitespace = (cur_whitespace / YP_TAB_WHITESPACE_SIZE + 1) * YP_TAB_WHITESPACE_SIZE; - } else { - cur_whitespace++; - } - cur_char++; - } - - // If we hit a newline, then we have encountered a line that contains - // only whitespace, and it shouldn't be considered in the calculation of - // common leading whitespace. - if (*cur_char == '\n') { - cur_char++; - continue; - } - - if (cur_whitespace < common_whitespace || common_whitespace == -1) { - common_whitespace = cur_whitespace; - } - - cur_char = memchr(cur_char + 1, '\n', (size_t) (parser->end - (cur_char + 1))); - if (cur_char) cur_char++; - } - } - } - - return common_whitespace; -} - -// Take a heredoc node that is indented by a ~ and trim the leading whitespace. -static void -parse_heredoc_dedent(yp_parser_t *parser, yp_node_t *node, yp_heredoc_quote_t quote) { - yp_node_list_t *nodes; - - if (quote == YP_HEREDOC_QUOTE_BACKTICK) { - nodes = &((yp_interpolated_x_string_node_t *) node)->parts; - } else { - nodes = &((yp_interpolated_string_node_t *) node)->parts; - } - - // First, calculate how much common whitespace we need to trim. If there is - // none or it's 0, then we can return early. - int common_whitespace; - if ((common_whitespace = parse_heredoc_common_whitespace(parser, nodes)) <= 0) return; - - // Iterate over all nodes, and trim whitespace accordingly. - for (size_t index = 0; index < nodes->size; index++) { - yp_node_t *node = nodes->nodes[index]; - if (node->type != YP_NODE_STRING_NODE) continue; - - // Get a reference to the string struct that is being held by the string - // node. This is the value we're going to actual manipulate. - yp_string_t *string = &((yp_string_node_t *) node)->unescaped; - yp_string_ensure_owned(string); - - // Now get the bounds of the existing string. We'll use this as a - // destination to move bytes into. We'll also use it for bounds checking - // since we don't require that these strings be null terminated. - size_t dest_length = string->as.owned.length; - char *source_start = string->as.owned.source; - - const char *source_cursor = source_start; - const char *source_end = source_cursor + dest_length; - - // We're going to move bytes backward in the string when we get leading - // whitespace, so we'll maintain a pointer to the current position in the - // string that we're writing to. - char *dest_cursor = source_start; - bool dedent_next = (index == 0) || (nodes->nodes[index - 1]->type == YP_NODE_STRING_NODE); - - while (source_cursor < source_end) { - // If we need to dedent the next element within the heredoc or the next - // line within the string node, then we'll do it here. - if (dedent_next) { - int trimmed_whitespace = 0; - - // While we haven't reached the amount of common whitespace that we need - // to trim and we haven't reached the end of the string, we'll keep - // trimming whitespace. Trimming in this context means skipping over - // these bytes such that they aren't copied into the new string. - while ((source_cursor < source_end) && yp_char_is_inline_whitespace(*source_cursor) && trimmed_whitespace < common_whitespace) { - if (*source_cursor == '\t') { - trimmed_whitespace = (trimmed_whitespace / YP_TAB_WHITESPACE_SIZE + 1) * YP_TAB_WHITESPACE_SIZE; - if (trimmed_whitespace > common_whitespace) break; - } else { - trimmed_whitespace++; - } - - source_cursor++; - dest_length--; - } - } - - // At this point we have dedented all that we need to, so we need to find - // the next newline. - const char *breakpoint = memchr(source_cursor, '\n', (size_t) (source_end - source_cursor)); - - if (breakpoint == NULL) { - // If there isn't another newline, then we can just move the rest of the - // string and break from the loop. - memmove(dest_cursor, source_cursor, (size_t) (source_end - source_cursor)); - break; - } - - // Otherwise, we need to move everything including the newline, and - // then set the dedent_next flag to true. - if (breakpoint < source_end) breakpoint++; - memmove(dest_cursor, source_cursor, (size_t) (breakpoint - source_cursor)); - dest_cursor += (breakpoint - source_cursor); - source_cursor = breakpoint; - dedent_next = true; - } - - string->as.owned.length = dest_length; - } -} - -static yp_node_t * -parse_pattern(yp_parser_t *parser, bool top_pattern, const char *message); - -// Accept any number of constants joined by :: delimiters. -static yp_node_t * -parse_pattern_constant_path(yp_parser_t *parser, yp_node_t *node) { - // Now, if there are any :: operators that follow, parse them as constant - // path nodes. - while (accept(parser, YP_TOKEN_COLON_COLON)) { - yp_token_t delimiter = parser->previous; - expect(parser, YP_TOKEN_CONSTANT, "Expected a constant after the :: operator."); - - yp_node_t *child = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); - node = (yp_node_t *)yp_constant_path_node_create(parser, node, &delimiter, child); - } - - // If there is a [ or ( that follows, then this is part of a larger pattern - // expression. We'll parse the inner pattern here, then modify the returned - // inner pattern with our constant path attached. - if (match_any_type_p(parser, 2, YP_TOKEN_BRACKET_LEFT, YP_TOKEN_PARENTHESIS_LEFT)) { - yp_token_t opening; - yp_token_t closing; - yp_node_t *inner = NULL; - - if (accept(parser, YP_TOKEN_BRACKET_LEFT)) { - opening = parser->previous; - - accept(parser, YP_TOKEN_NEWLINE); - - if (!accept(parser, YP_TOKEN_BRACKET_RIGHT)) { - inner = parse_pattern(parser, true, "Expected a pattern expression after the [ operator."); - accept(parser, YP_TOKEN_NEWLINE); - - expect(parser, YP_TOKEN_BRACKET_RIGHT, "Expected a ] to close the pattern expression."); - } - - closing = parser->previous; - } else { - parser_lex(parser); - opening = parser->previous; - - if (!accept(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { - inner = parse_pattern(parser, true, "Expected a pattern expression after the ( operator."); - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a ) to close the pattern expression."); - } - - closing = parser->previous; - } - - if (inner) { - // Now that we have the inner pattern, check to see if it's an array, find, - // or hash pattern. If it is, then we'll attach our constant path to it. If - // it's not, then we'll create an array pattern. - switch (inner->type) { - case YP_NODE_ARRAY_PATTERN_NODE: { - yp_array_pattern_node_t *pattern_node = (yp_array_pattern_node_t *)inner; - pattern_node->base.location.start = node->location.start; - pattern_node->base.location.end = closing.end; - - pattern_node->constant = node; - pattern_node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; - pattern_node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; - - node = (yp_node_t *)pattern_node; - break; - } - case YP_NODE_FIND_PATTERN_NODE: { - yp_find_pattern_node_t *pattern_node = (yp_find_pattern_node_t *) inner; - pattern_node->base.location.start = node->location.start; - pattern_node->base.location.end = closing.end; - - pattern_node->constant = node; - pattern_node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; - pattern_node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; - - node = (yp_node_t *) pattern_node; - break; - } - case YP_NODE_HASH_PATTERN_NODE: { - yp_hash_pattern_node_t *pattern_node = (yp_hash_pattern_node_t *)inner; - pattern_node->base.location.start = node->location.start; - pattern_node->base.location.end = closing.end; - - pattern_node->constant = node; - pattern_node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; - pattern_node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; - - node = (yp_node_t *) pattern_node; - break; - } - default: { - yp_array_pattern_node_t *pattern_node = yp_array_pattern_node_constant_create(parser, node, &opening, &closing); - yp_array_pattern_node_requireds_append(pattern_node, inner); - node = (yp_node_t *)pattern_node; - break; - } - } - } else { - // If there was no inner pattern, then we have something like Foo() or - // Foo[]. In that case we'll create an array pattern with no requireds. - node = (yp_node_t *)yp_array_pattern_node_constant_create(parser, node, &opening, &closing); - } - } - - return node; -} - -// Parse a rest pattern. -static yp_splat_node_t * -parse_pattern_rest(yp_parser_t *parser) { - assert(parser->previous.type == YP_TOKEN_USTAR); - yp_token_t operator = parser->previous; - yp_node_t *name = NULL; - - // Rest patterns don't necessarily have a name associated with them. So we - // will check for that here. If they do, then we'll add it to the local table - // since this pattern will cause it to become a local variable. - if (accept(parser, YP_TOKEN_IDENTIFIER)) { - yp_token_t identifier = parser->previous; - yp_parser_local_add(parser, &identifier); - name = (yp_node_t *) yp_local_variable_target_node_create(parser, &identifier); - } - - // Finally we can return the created node. - return yp_splat_node_create(parser, &operator, name); -} - -// Parse a keyword rest node. -static yp_node_t * -parse_pattern_keyword_rest(yp_parser_t *parser) { - assert(parser->current.type == YP_TOKEN_USTAR_STAR); - parser_lex(parser); - - yp_token_t operator = parser->previous; - yp_node_t *value = NULL; - - if (accept(parser, YP_TOKEN_KEYWORD_NIL)) { - return (yp_node_t *) yp_no_keywords_parameter_node_create(parser, &operator, &parser->previous); - } - - if (accept(parser, YP_TOKEN_IDENTIFIER)) { - yp_parser_local_add(parser, &parser->previous); - value = (yp_node_t *) yp_local_variable_target_node_create(parser, &parser->previous); - } - - return (yp_node_t *) yp_assoc_splat_node_create(parser, value, &operator); -} - -// Parse a hash pattern. -static yp_hash_pattern_node_t * -parse_pattern_hash(yp_parser_t *parser, yp_node_t *first_assoc) { - if (!match_any_type_p(parser, 7, YP_TOKEN_COMMA, YP_TOKEN_KEYWORD_THEN, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_PARENTHESIS_RIGHT, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { - // Here we have a value for the first assoc in the list, so we will parse it - // now and update the first assoc. - yp_node_t *value = parse_pattern(parser, false, "Expected a pattern expression after the key."); - - assert(first_assoc->type == YP_NODE_ASSOC_NODE); - yp_assoc_node_t *assoc = (yp_assoc_node_t *) first_assoc; - assoc->base.location.end = value->location.end; - assoc->value = value; - } - - yp_node_list_t assocs; - yp_node_list_init(&assocs); - yp_node_list_append(&assocs, first_assoc); - - // If there are any other assocs, then we'll parse them now. - while (accept(parser, YP_TOKEN_COMMA)) { - // Here we need to break to support trailing commas. - if (match_any_type_p(parser, 6, YP_TOKEN_KEYWORD_THEN, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_PARENTHESIS_RIGHT, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { - break; - } - - yp_node_t *assoc; - - if (match_type_p(parser, YP_TOKEN_USTAR_STAR)) { - assoc = parse_pattern_keyword_rest(parser); - } else { - expect(parser, YP_TOKEN_LABEL, "Expected a label after the `,'."); - yp_node_t *key = (yp_node_t *) yp_symbol_node_label_create(parser, &parser->previous); - yp_node_t *value = NULL; - - if (!match_any_type_p(parser, 7, YP_TOKEN_COMMA, YP_TOKEN_KEYWORD_THEN, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_PARENTHESIS_RIGHT, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { - value = parse_pattern(parser, false, "Expected a pattern expression after the key."); - } - - yp_token_t operator = not_provided(parser); - assoc = (yp_node_t *) yp_assoc_node_create(parser, key, &operator, value); - } - - yp_node_list_append(&assocs, assoc); - } - - yp_hash_pattern_node_t *node = yp_hash_pattern_node_node_list_create(parser, &assocs); - free(assocs.nodes); - - return node; -} - -// Parse a pattern expression primitive. -static yp_node_t * -parse_pattern_primitive(yp_parser_t *parser, const char *message) { - switch (parser->current.type) { - case YP_TOKEN_IDENTIFIER: { - parser_lex(parser); - yp_parser_local_add(parser, &parser->previous); - return (yp_node_t *) yp_local_variable_target_node_create(parser, &parser->previous); - } - case YP_TOKEN_BRACKET_LEFT_ARRAY: { - yp_token_t opening = parser->current; - parser_lex(parser); - - if (accept(parser, YP_TOKEN_BRACKET_RIGHT)) { - // If we have an empty array pattern, then we'll just return a new - // array pattern node. - return (yp_node_t *)yp_array_pattern_node_empty_create(parser, &opening, &parser->previous); - } - - // Otherwise, we'll parse the inner pattern, then deal with it depending - // on the type it returns. - yp_node_t *inner = parse_pattern(parser, true, "Expected a pattern expression after the [ operator."); - - accept(parser, YP_TOKEN_NEWLINE); - - expect(parser, YP_TOKEN_BRACKET_RIGHT, "Expected a ] to close the pattern expression."); - yp_token_t closing = parser->previous; - - switch (inner->type) { - case YP_NODE_ARRAY_PATTERN_NODE: { - yp_array_pattern_node_t *pattern_node = (yp_array_pattern_node_t *) inner; - if (pattern_node->opening_loc.start == NULL) { - pattern_node->base.location.start = opening.start; - pattern_node->base.location.end = closing.end; - - pattern_node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; - pattern_node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; - - return (yp_node_t *) pattern_node; - } - - break; - } - case YP_NODE_FIND_PATTERN_NODE: { - yp_find_pattern_node_t *pattern_node = (yp_find_pattern_node_t *) inner; - if (pattern_node->opening_loc.start == NULL) { - pattern_node->base.location.start = opening.start; - pattern_node->base.location.end = closing.end; - - pattern_node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; - pattern_node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; - - return (yp_node_t *) pattern_node; - } - - break; - } - default: - break; - } - - yp_array_pattern_node_t *node = yp_array_pattern_node_empty_create(parser, &opening, &closing); - yp_array_pattern_node_requireds_append(node, inner); - return (yp_node_t *) node; - } - case YP_TOKEN_BRACE_LEFT: { - bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; - parser->pattern_matching_newlines = false; - - yp_hash_pattern_node_t *node; - yp_token_t opening = parser->current; - parser_lex(parser); - - if (accept(parser, YP_TOKEN_BRACE_RIGHT)) { - // If we have an empty hash pattern, then we'll just return a new hash - // pattern node. - node = yp_hash_pattern_node_empty_create(parser, &opening, &parser->previous); - } else { - yp_node_t *key; - - switch (parser->current.type) { - case YP_TOKEN_LABEL: - parser_lex(parser); - key = (yp_node_t *) yp_symbol_node_label_create(parser, &parser->previous); - break; - case YP_TOKEN_USTAR_STAR: - key = parse_pattern_keyword_rest(parser); - break; - case YP_TOKEN_STRING_BEGIN: - key = parse_expression(parser, YP_BINDING_POWER_MAX, "Expected a key in the hash pattern."); - if (!yp_symbol_node_label_p(key)) { - yp_diagnostic_list_append(&parser->error_list, key->location.start, key->location.end, "Expected a label as the key in the hash pattern."); - } - - break; - default: - parser_lex(parser); - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Expected a key in the hash pattern."); - key = (yp_node_t *) yp_missing_node_create(parser, parser->previous.start, parser->previous.end); - break; - } - - yp_token_t operator = not_provided(parser); - node = parse_pattern_hash(parser, (yp_node_t *) yp_assoc_node_create(parser, key, &operator, NULL)); - - accept(parser, YP_TOKEN_NEWLINE); - expect(parser, YP_TOKEN_BRACE_RIGHT, "Expected a } to close the pattern expression."); - yp_token_t closing = parser->previous; - - node->base.location.start = opening.start; - node->base.location.end = closing.end; - - node->opening_loc = (yp_location_t) { .start = opening.start, .end = opening.end }; - node->closing_loc = (yp_location_t) { .start = closing.start, .end = closing.end }; - } - - parser->pattern_matching_newlines = previous_pattern_matching_newlines; - return (yp_node_t *) node; - } - case YP_TOKEN_UDOT_DOT: - case YP_TOKEN_UDOT_DOT_DOT: { - yp_token_t operator = parser->current; - parser_lex(parser); - - // Since we have a unary range operator, we need to parse the subsequent - // expression as the right side of the range. - switch (parser->current.type) { - case YP_CASE_PRIMITIVE: { - yp_node_t *right = parse_expression(parser, YP_BINDING_POWER_MAX, "Expected an expression after the range operator."); - return (yp_node_t *) yp_range_node_create(parser, NULL, &operator, right); - } - default: { - yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "Expected an expression after the range operator."); - yp_node_t *right = (yp_node_t *) yp_missing_node_create(parser, operator.start, operator.end); - return (yp_node_t *) yp_range_node_create(parser, NULL, &operator, right); - } - } - } - case YP_CASE_PRIMITIVE: { - yp_node_t *node = parse_expression(parser, YP_BINDING_POWER_MAX, message); - - // Now that we have a primitive, we need to check if it's part of a range. - if (accept_any(parser, 2, YP_TOKEN_DOT_DOT, YP_TOKEN_DOT_DOT_DOT)) { - yp_token_t operator = parser->previous; - - // Now that we have the operator, we need to check if this is followed - // by another expression. If it is, then we will create a full range - // node. Otherwise, we'll create an endless range. - switch (parser->current.type) { - case YP_CASE_PRIMITIVE: { - yp_node_t *right = parse_expression(parser, YP_BINDING_POWER_MAX, "Expected an expression after the range operator."); - return (yp_node_t *) yp_range_node_create(parser, node, &operator, right); - } - default: - return (yp_node_t *) yp_range_node_create(parser, node, &operator, NULL); - } - } - - return node; - } - case YP_TOKEN_CARET: { - parser_lex(parser); - yp_token_t operator = parser->previous; - - // At this point we have a pin operator. We need to check the subsequent - // expression to determine if it's a variable or an expression. - switch (parser->current.type) { - case YP_TOKEN_IDENTIFIER: { - parser_lex(parser); - yp_node_t *variable = (yp_node_t *) yp_local_variable_read_node_create(parser, &parser->previous, 0); - - return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); - } - case YP_TOKEN_INSTANCE_VARIABLE: { - parser_lex(parser); - yp_node_t *variable = (yp_node_t *) yp_instance_variable_read_node_create(parser, &parser->previous); - - return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); - } - case YP_TOKEN_CLASS_VARIABLE: { - parser_lex(parser); - yp_node_t *variable = (yp_node_t *) yp_class_variable_read_node_create(parser, &parser->previous); - - return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); - } - case YP_TOKEN_GLOBAL_VARIABLE: - case YP_TOKEN_NTH_REFERENCE: - case YP_TOKEN_BACK_REFERENCE: { - parser_lex(parser); - yp_global_variable_read_node_t *variable = yp_global_variable_read_node_create(parser, &parser->previous); - return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, (yp_node_t *) variable); - } - case YP_TOKEN_PARENTHESIS_LEFT: { - bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; - parser->pattern_matching_newlines = false; - - yp_token_t lparen = parser->current; - parser_lex(parser); - - yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected an expression after the pin operator."); - parser->pattern_matching_newlines = previous_pattern_matching_newlines; - - accept(parser, YP_TOKEN_NEWLINE); - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a closing parenthesis after the expression."); - return (yp_node_t *) yp_pinned_expression_node_create(parser, expression, &operator, &lparen, &parser->previous); - } - default: { - // If we get here, then we have a pin operator followed by something - // not understood. We'll create a missing node and return that. - yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "Expected a variable after the pin operator."); - yp_node_t *variable = (yp_node_t *) yp_missing_node_create(parser, operator.start, operator.end); - return (yp_node_t *) yp_pinned_variable_node_create(parser, &operator, variable); - } - } - } - case YP_TOKEN_UCOLON_COLON: { - yp_token_t delimiter = parser->current; - parser_lex(parser); - - expect(parser, YP_TOKEN_CONSTANT, "Expected a constant after the :: operator."); - yp_node_t *child = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); - yp_constant_path_node_t *node = yp_constant_path_node_create(parser, NULL, &delimiter, child); - - return parse_pattern_constant_path(parser, (yp_node_t *)node); - } - case YP_TOKEN_CONSTANT: { - yp_token_t constant = parser->current; - parser_lex(parser); - - yp_node_t *node = (yp_node_t *) yp_constant_read_node_create(parser, &constant); - return parse_pattern_constant_path(parser, node); - } - default: - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, message); - return (yp_node_t *) yp_missing_node_create(parser, parser->current.start, parser->current.end); - } -} - -// Parse any number of primitives joined by alternation and ended optionally by -// assignment. -static yp_node_t * -parse_pattern_primitives(yp_parser_t *parser, const char *message) { - yp_node_t *node = NULL; - - do { - yp_token_t operator = parser->previous; - - switch (parser->current.type) { - case YP_TOKEN_IDENTIFIER: - case YP_TOKEN_BRACKET_LEFT_ARRAY: - case YP_TOKEN_BRACE_LEFT: - case YP_TOKEN_CARET: - case YP_TOKEN_CONSTANT: - case YP_TOKEN_UCOLON_COLON: - case YP_TOKEN_UDOT_DOT: - case YP_TOKEN_UDOT_DOT_DOT: - case YP_CASE_PRIMITIVE: { - if (node == NULL) { - node = parse_pattern_primitive(parser, message); - } else { - yp_node_t *right = parse_pattern_primitive(parser, "Expected to be able to parse a pattern after `|'."); - node = (yp_node_t *) yp_alternation_pattern_node_create(parser, node, right, &operator); - } - - break; - } - case YP_TOKEN_PARENTHESIS_LEFT: { - parser_lex(parser); - if (node != NULL) { - yp_node_destroy(parser, node); - } - node = parse_pattern(parser, false, "Expected a pattern after the opening parenthesis."); - - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a closing parenthesis after the pattern."); - break; - } - default: { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, message); - yp_node_t *right = (yp_node_t *) yp_missing_node_create(parser, parser->current.start, parser->current.end); - - if (node == NULL) { - node = right; - } else { - node = (yp_node_t *) yp_alternation_pattern_node_create(parser, node, right, &operator); - } - - break; - } - } - } while (accept(parser, YP_TOKEN_PIPE)); - - // If we have an =>, then we are assigning this pattern to a variable. - // In this case we should create an assignment node. - while (accept(parser, YP_TOKEN_EQUAL_GREATER)) { - yp_token_t operator = parser->previous; - - expect(parser, YP_TOKEN_IDENTIFIER, "Expected an identifier after the `=>' operator."); - yp_token_t identifier = parser->previous; - yp_parser_local_add(parser, &identifier); - - yp_node_t *target = (yp_node_t *) yp_local_variable_target_node_create(parser, &identifier); - node = (yp_node_t *) yp_capture_pattern_node_create(parser, node, target, &operator); - } - - return node; -} - -// Parse a pattern matching expression. -static yp_node_t * -parse_pattern(yp_parser_t *parser, bool top_pattern, const char *message) { - yp_node_t *node = NULL; - - bool leading_rest = false; - bool trailing_rest = false; - - switch (parser->current.type) { - case YP_TOKEN_LABEL: { - parser_lex(parser); - yp_node_t *key = (yp_node_t *) yp_symbol_node_label_create(parser, &parser->previous); - yp_token_t operator = not_provided(parser); - - return (yp_node_t *) parse_pattern_hash(parser, (yp_node_t *) yp_assoc_node_create(parser, key, &operator, NULL)); - } - case YP_TOKEN_USTAR_STAR: { - node = parse_pattern_keyword_rest(parser); - return (yp_node_t *) parse_pattern_hash(parser, node); - } - case YP_TOKEN_USTAR: { - if (top_pattern) { - parser_lex(parser); - node = (yp_node_t *) parse_pattern_rest(parser); - leading_rest = true; - break; - } - } - /* fallthrough */ - default: - node = parse_pattern_primitives(parser, message); - break; - } - - // If we got a dynamic label symbol, then we need to treat it like the - // beginning of a hash pattern. - if (yp_symbol_node_label_p(node)) { - yp_token_t operator = not_provided(parser); - return (yp_node_t *) parse_pattern_hash(parser, (yp_node_t *) yp_assoc_node_create(parser, node, &operator, NULL)); - } - - if (top_pattern && match_type_p(parser, YP_TOKEN_COMMA)) { - // If we have a comma, then we are now parsing either an array pattern or a - // find pattern. We need to parse all of the patterns, put them into a big - // list, and then determine which type of node we have. - yp_node_list_t nodes; - yp_node_list_init(&nodes); - yp_node_list_append(&nodes, node); - - // Gather up all of the patterns into the list. - while (accept(parser, YP_TOKEN_COMMA)) { - // Break early here in case we have a trailing comma. - if (match_any_type_p(parser, 5, YP_TOKEN_KEYWORD_THEN, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { - break; - } - - if (accept(parser, YP_TOKEN_USTAR)) { - node = (yp_node_t *) parse_pattern_rest(parser); - - // If we have already parsed a splat pattern, then this is an error. We - // will continue to parse the rest of the patterns, but we will indicate - // it as an error. - if (trailing_rest) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected rest pattern."); - } - - trailing_rest = true; - } else { - node = parse_pattern_primitives(parser, "Expected a pattern after the comma."); - } - - yp_node_list_append(&nodes, node); - } - - // If the first pattern and the last pattern are rest patterns, then we will - // call this a find pattern, regardless of how many rest patterns are in - // between because we know we already added the appropriate errors. - // Otherwise we will create an array pattern. - if (nodes.nodes[0]->type == YP_NODE_SPLAT_NODE && nodes.nodes[nodes.size - 1]->type == YP_NODE_SPLAT_NODE) { - node = (yp_node_t *) yp_find_pattern_node_create(parser, &nodes); - } else { - node = (yp_node_t *) yp_array_pattern_node_node_list_create(parser, &nodes); - } - - free(nodes.nodes); - } else if (leading_rest) { - // Otherwise, if we parsed a single splat pattern, then we know we have an - // array pattern, so we can go ahead and create that node. - node = (yp_node_t *) yp_array_pattern_node_rest_create(parser, node); - } - - return node; -} - -// Parse an expression that begins with the previous node that we just lexed. -static inline yp_node_t * -parse_expression_prefix(yp_parser_t *parser, yp_binding_power_t binding_power) { - yp_lex_mode_t *lex_mode = parser->lex_modes.current; - - switch (parser->current.type) { - case YP_TOKEN_BRACKET_LEFT_ARRAY: { - parser_lex(parser); - - yp_token_t opening = parser->previous; - yp_array_node_t *array = yp_array_node_create(parser, &opening, &opening); - yp_accepts_block_stack_push(parser, true); - bool parsed_bare_hash = false; - - while (!match_any_type_p(parser, 2, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_EOF)) { - // Handle the case where we don't have a comma and we have a newline followed by a right bracket. - if (accept(parser, YP_TOKEN_NEWLINE) && match_type_p(parser, YP_TOKEN_BRACKET_RIGHT)) { - break; - } - - if (yp_array_node_size(array) != 0) { - expect(parser, YP_TOKEN_COMMA, "Expected a separator for the elements in an array."); - } - - // If we have a right bracket immediately following a comma, this is - // allowed since it's a trailing comma. In this case we can break out of - // the loop. - if (match_type_p(parser, YP_TOKEN_BRACKET_RIGHT)) break; - - yp_node_t *element; - - if (accept(parser, YP_TOKEN_USTAR)) { - yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected an expression after '*' in the array."); - element = (yp_node_t *) yp_splat_node_create(parser, &parser->previous, expression); - } else if (match_any_type_p(parser, 2, YP_TOKEN_LABEL, YP_TOKEN_USTAR_STAR)) { - if (parsed_bare_hash) { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Unexpected bare hash."); - } - - yp_keyword_hash_node_t *hash = yp_keyword_hash_node_create(parser); - element = (yp_node_t *)hash; - - if (!match_any_type_p(parser, 8, YP_TOKEN_EOF, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON, YP_TOKEN_EOF, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_BRACKET_RIGHT, YP_TOKEN_KEYWORD_DO, YP_TOKEN_PARENTHESIS_RIGHT)) { - parse_assocs(parser, (yp_node_t *) hash); - } - - parsed_bare_hash = true; - } else { - element = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected an element for the array."); - - if (yp_symbol_node_label_p(element) || accept(parser, YP_TOKEN_EQUAL_GREATER)) { - if (parsed_bare_hash) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected bare hash."); - } - - yp_keyword_hash_node_t *hash = yp_keyword_hash_node_create(parser); - - yp_token_t operator; - if (parser->previous.type == YP_TOKEN_EQUAL_GREATER) { - operator = parser->previous; - } else { - operator = not_provided(parser); - } - - yp_node_t *value = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value in the hash literal."); - yp_node_t *assoc = (yp_node_t *) yp_assoc_node_create(parser, element, &operator, value); - yp_keyword_hash_node_elements_append(hash, assoc); - - element = (yp_node_t *)hash; - if (accept(parser, YP_TOKEN_COMMA) && !match_type_p(parser, YP_TOKEN_BRACKET_RIGHT)) { - parse_assocs(parser, (yp_node_t *) hash); - } - - parsed_bare_hash = true; - } - } - - yp_array_node_elements_append(array, element); - if (element->type == YP_NODE_MISSING_NODE) break; - } - - accept(parser, YP_TOKEN_NEWLINE); - expect(parser, YP_TOKEN_BRACKET_RIGHT, "Expected a closing bracket for the array."); - yp_array_node_close_set(array, &parser->previous); - yp_accepts_block_stack_pop(parser); - - return (yp_node_t *) array; - } - case YP_TOKEN_PARENTHESIS_LEFT: - case YP_TOKEN_PARENTHESIS_LEFT_PARENTHESES: { - parser_lex(parser); - - yp_token_t opening = parser->previous; - while (accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE)); - - // If this is the end of the file or we match a right parenthesis, then - // we have an empty parentheses node, and we can immediately return. - if (match_any_type_p(parser, 2, YP_TOKEN_PARENTHESIS_RIGHT, YP_TOKEN_EOF)) { - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a closing parenthesis."); - return (yp_node_t *) yp_parentheses_node_create(parser, &opening, NULL, &parser->previous); - } - - // Otherwise, we're going to parse the first statement in the list of - // statements within the parentheses. - yp_accepts_block_stack_push(parser, true); - yp_node_t *statement = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected to be able to parse an expression."); - while (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)); - - // If we hit a right parenthesis, then we're done parsing the parentheses - // node, and we can check which kind of node we should return. - if (accept(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { - yp_accepts_block_stack_pop(parser); - - // If we have a single statement and are ending on a right parenthesis, - // then we need to check if this is possibly a multiple assignment node. - if (binding_power == YP_BINDING_POWER_STATEMENT && statement->type == YP_NODE_MULTI_WRITE_NODE) { - yp_multi_write_node_t *multi_statement = (yp_multi_write_node_t *) statement; - - if (multi_statement->value == NULL) { - yp_location_t lparen_loc = { .start = opening.start, .end = opening.end }; - yp_location_t rparen_loc = { .start = parser->previous.start, .end = parser->previous.end }; - yp_multi_write_node_t *multi_write; - - if (multi_statement->lparen_loc.start == NULL) { - multi_write = (yp_multi_write_node_t *) statement; - multi_write->lparen_loc = lparen_loc; - multi_write->rparen_loc = rparen_loc; - } else { - yp_token_t operator = not_provided(parser); - multi_write = yp_multi_write_node_create(parser, &operator, NULL, &lparen_loc, &rparen_loc); - yp_multi_write_node_targets_append(multi_write, statement); - } - - return parse_targets(parser, (yp_node_t *) multi_write, YP_BINDING_POWER_INDEX); - } - } - - // If we have a single statement and are ending on a right parenthesis - // and we didn't return a multiple assignment node, then we can return a - // regular parentheses node now. - yp_statements_node_t *statements = yp_statements_node_create(parser); - yp_statements_node_body_append(statements, statement); - - return (yp_node_t *) yp_parentheses_node_create(parser, &opening, (yp_node_t *) statements, &parser->previous); - } - - // If we have more than one statement in the set of parentheses, then we - // are going to parse all of them as a list of statements. We'll do that - // here. - context_push(parser, YP_CONTEXT_PARENS); - yp_statements_node_t *statements = yp_statements_node_create(parser); - yp_statements_node_body_append(statements, statement); - - while (!match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { - // Ignore semicolon without statements before them - if (accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE)) continue; - - yp_node_t *node = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected to be able to parse an expression."); - yp_statements_node_body_append(statements, node); - - // If we're recovering from a syntax error, then we need to stop parsing the - // statements now. - if (parser->recovering) { - // If this is the level of context where the recovery has happened, then - // we can mark the parser as done recovering. - if (match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) parser->recovering = false; - break; - } - - if (!accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) break; - } - - context_pop(parser); - yp_accepts_block_stack_pop(parser); - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected a closing parenthesis."); - - return (yp_node_t *) yp_parentheses_node_create(parser, &opening, (yp_node_t *) statements, &parser->previous); - } - case YP_TOKEN_BRACE_LEFT: { - yp_accepts_block_stack_push(parser, true); - parser_lex(parser); - - yp_token_t opening = parser->previous; - yp_hash_node_t *node = yp_hash_node_create(parser, &opening, &opening); - - if (!match_any_type_p(parser, 2, YP_TOKEN_BRACE_RIGHT, YP_TOKEN_EOF)) { - parse_assocs(parser, (yp_node_t *) node); - accept(parser, YP_TOKEN_NEWLINE); - } - - yp_accepts_block_stack_pop(parser); - expect(parser, YP_TOKEN_BRACE_RIGHT, "Expected a closing delimiter for a hash literal."); - node->closing = parser->previous; - node->base.location.end = parser->previous.end; - - return (yp_node_t *)node; - } - case YP_TOKEN_CHARACTER_LITERAL: { - parser_lex(parser); - - yp_token_t opening = parser->previous; - opening.type = YP_TOKEN_STRING_BEGIN; - opening.end = opening.start + 1; - - yp_token_t content = parser->previous; - content.type = YP_TOKEN_STRING_CONTENT; - content.start = content.start + 1; - - yp_token_t closing = not_provided(parser); - - return (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &content, &closing, YP_UNESCAPE_ALL); - } - case YP_TOKEN_CLASS_VARIABLE: { - parser_lex(parser); - yp_node_t *node = (yp_node_t *) yp_class_variable_read_node_create(parser, &parser->previous); - - if (binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { - node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); - } - - return node; - } - case YP_TOKEN_CONSTANT: { - parser_lex(parser); - yp_token_t constant = parser->previous; - - // If a constant is immediately followed by parentheses, then this is in - // fact a method call, not a constant read. - if ( - match_type_p(parser, YP_TOKEN_PARENTHESIS_LEFT) || - (binding_power <= YP_BINDING_POWER_ASSIGNMENT && (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR))) || - (yp_accepts_block_stack_p(parser) && match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_DO, YP_TOKEN_BRACE_LEFT)) - ) { - yp_arguments_t arguments = yp_arguments(parser); - parse_arguments_list(parser, &arguments, true); - return (yp_node_t *) yp_call_node_fcall_create(parser, &constant, &arguments); - } - - yp_node_t *node = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); - - if ((binding_power == YP_BINDING_POWER_STATEMENT) && match_type_p(parser, YP_TOKEN_COMMA)) { - // If we get here, then we have a comma immediately following a - // constant, so we're going to parse this as a multiple assignment. - node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); - } - - return node; - } - case YP_TOKEN_UCOLON_COLON: { - parser_lex(parser); - - yp_token_t delimiter = parser->previous; - expect(parser, YP_TOKEN_CONSTANT, "Expected a constant after ::."); - - yp_node_t *constant = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); - yp_node_t *node = (yp_node_t *)yp_constant_path_node_create(parser, NULL, &delimiter, constant); - - if ((binding_power == YP_BINDING_POWER_STATEMENT) && match_type_p(parser, YP_TOKEN_COMMA)) { - node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); - } - - return node; - } - case YP_TOKEN_UDOT_DOT: - case YP_TOKEN_UDOT_DOT_DOT: { - yp_token_t operator = parser->current; - parser_lex(parser); - - yp_node_t *right = parse_expression(parser, binding_power, "Expected a value after the operator."); - return (yp_node_t *) yp_range_node_create(parser, NULL, &operator, right); - } - case YP_TOKEN_FLOAT: - parser_lex(parser); - return (yp_node_t *)yp_float_node_create(parser, &parser->previous); - case YP_TOKEN_NTH_REFERENCE: - case YP_TOKEN_GLOBAL_VARIABLE: - case YP_TOKEN_BACK_REFERENCE: { - parser_lex(parser); - yp_node_t *node = (yp_node_t *) yp_global_variable_read_node_create(parser, &parser->previous); - - if (binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { - node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); - } - - return node; - } - case YP_TOKEN_IDENTIFIER: { - parser_lex(parser); - yp_token_t identifier = parser->previous; - yp_node_t *node = parse_vcall(parser); - - if (node->type == YP_NODE_CALL_NODE) { - // If parse_vcall returned with a call node, then we know the identifier - // is not in the local table. In that case we need to check if there are - // arguments following the identifier. - yp_call_node_t *call = (yp_call_node_t *) node; - yp_arguments_t arguments = yp_arguments(parser); - parse_arguments_list(parser, &arguments, true); - - call->opening = arguments.opening; - call->arguments = arguments.arguments; - call->closing = arguments.closing; - call->block = arguments.block; - - if (arguments.block != NULL) { - call->base.location.end = arguments.block->base.location.end; - } else if (arguments.closing.type == YP_TOKEN_NOT_PROVIDED) { - if (arguments.arguments != NULL) { - call->base.location.end = arguments.arguments->base.location.end; - } else { - call->base.location.end = call->message.end; - } - } else { - call->base.location.end = arguments.closing.end; - } - } else { - // Otherwise, we know the identifier is in the local table. This can - // still be a method call if it is followed by arguments or a block, so - // we need to check for that here. - if ( - (binding_power <= YP_BINDING_POWER_ASSIGNMENT && (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR))) || - (yp_accepts_block_stack_p(parser) && match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_DO, YP_TOKEN_BRACE_LEFT)) - ) { - yp_arguments_t arguments = yp_arguments(parser); - parse_arguments_list(parser, &arguments, true); - - yp_call_node_t *fcall = yp_call_node_fcall_create(parser, &identifier, &arguments); - yp_node_destroy(parser, node); - return (yp_node_t *) fcall; - } - } - - if ((binding_power == YP_BINDING_POWER_STATEMENT) && match_type_p(parser, YP_TOKEN_COMMA)) { - node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); - } - - return node; - } - case YP_TOKEN_HEREDOC_START: { - assert(parser->lex_modes.current->mode == YP_LEX_HEREDOC); - yp_heredoc_quote_t quote = parser->lex_modes.current->as.heredoc.quote; - yp_heredoc_indent_t indent = parser->lex_modes.current->as.heredoc.indent; - - yp_node_t *node; - if (quote == YP_HEREDOC_QUOTE_BACKTICK) { - node = (yp_node_t *) yp_interpolated_xstring_node_create(parser, &parser->current, &parser->current); - } else { - node = (yp_node_t *) yp_interpolated_string_node_create(parser, &parser->current, NULL, &parser->current); - } - - parser_lex(parser); - yp_node_t *part; - - while (!match_any_type_p(parser, 2, YP_TOKEN_HEREDOC_END, YP_TOKEN_EOF)) { - if ((part = parse_string_part(parser)) == NULL) continue; - - if (quote == YP_HEREDOC_QUOTE_BACKTICK) { - yp_interpolated_xstring_node_append((yp_interpolated_x_string_node_t *) node, part); - } else { - yp_interpolated_string_node_append((yp_interpolated_string_node_t *) node, part); - } - } - - lex_state_set(parser, YP_LEX_STATE_END); - expect(parser, YP_TOKEN_HEREDOC_END, "Expected a closing delimiter for heredoc."); - if (quote == YP_HEREDOC_QUOTE_BACKTICK) { - assert(node->type == YP_NODE_INTERPOLATED_X_STRING_NODE); - yp_interpolated_xstring_node_closing_set(((yp_interpolated_x_string_node_t *) node), &parser->previous); - } else { - assert(node->type == YP_NODE_INTERPOLATED_STRING_NODE); - yp_interpolated_string_node_closing_set((yp_interpolated_string_node_t *) node, &parser->previous); - } - - // If this is a heredoc that is indented with a ~, then we need to dedent - // each line by the common leading whitespace. - if (indent == YP_HEREDOC_INDENT_TILDE) { - parse_heredoc_dedent(parser, node, quote); - } - - // If there's a string immediately following this heredoc, then it's a - // concatenatation. In this case we'll parse the next string and create a - // node in the tree that concatenates the two strings. - if (parser->current.type == YP_TOKEN_STRING_BEGIN) { - return (yp_node_t *) yp_string_concat_node_create( - parser, - node, - parse_expression(parser, YP_BINDING_POWER_CALL, "Expected string on the right side of concatenation.") - ); - } else { - return node; - } - } - case YP_TOKEN_IMAGINARY_NUMBER: - parser_lex(parser); - return (yp_node_t *) yp_imaginary_node_create(parser, &parser->previous); - case YP_TOKEN_INSTANCE_VARIABLE: { - parser_lex(parser); - yp_node_t *node = (yp_node_t *) yp_instance_variable_read_node_create(parser, &parser->previous); - - if (binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { - node = parse_targets(parser, node, YP_BINDING_POWER_INDEX); - } - - return node; - } - case YP_TOKEN_INTEGER: - parser_lex(parser); - return (yp_node_t *) yp_integer_node_create(parser, &parser->previous); - case YP_TOKEN_KEYWORD___ENCODING__: - parser_lex(parser); - return (yp_node_t *) yp_source_encoding_node_create(parser, &parser->previous); - case YP_TOKEN_KEYWORD___FILE__: - parser_lex(parser); - return (yp_node_t *) yp_source_file_node_create(parser, &parser->previous); - case YP_TOKEN_KEYWORD___LINE__: - parser_lex(parser); - return (yp_node_t *) yp_source_line_node_create(parser, &parser->previous); - case YP_TOKEN_KEYWORD_ALIAS: { - parser_lex(parser); - yp_token_t keyword = parser->previous; - - yp_node_t *left = parse_alias_argument(parser, true); - yp_node_t *right = parse_alias_argument(parser, false); - - switch (left->type) { - case YP_NODE_SYMBOL_NODE: - case YP_NODE_INTERPOLATED_SYMBOL_NODE: { - if (right->type != YP_NODE_SYMBOL_NODE && right->type != YP_NODE_INTERPOLATED_SYMBOL_NODE) { - yp_diagnostic_list_append(&parser->error_list, right->location.start, right->location.end, "Expected a bare word or symbol argument."); - } - break; - } - case YP_NODE_GLOBAL_VARIABLE_READ_NODE: { - if (right->type == YP_NODE_GLOBAL_VARIABLE_READ_NODE) { - yp_token_t *name = &((yp_global_variable_read_node_t *) right)->name; - - if (name->type == YP_TOKEN_NTH_REFERENCE) { - yp_diagnostic_list_append(&parser->error_list, right->location.start, right->location.end, "Can't make alias for number variables."); - } - } else { - yp_diagnostic_list_append(&parser->error_list, right->location.start, right->location.end, "Expected a global variable."); - } - break; - } - default: - break; - } - - return (yp_node_t *) yp_alias_node_create(parser, &keyword, left, right); - } - case YP_TOKEN_KEYWORD_CASE: { - parser_lex(parser); - yp_token_t case_keyword = parser->previous; - yp_node_t *predicate = NULL; - - if ( - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON) || - match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_WHEN, YP_TOKEN_KEYWORD_IN, YP_TOKEN_KEYWORD_END) || - !token_begins_expression_p(parser->current.type) - ) { - predicate = NULL; - } else { - predicate = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected a value after case keyword."); - while (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)); - } - - if (accept(parser, YP_TOKEN_KEYWORD_END)) { - return (yp_node_t *) yp_case_node_create(parser, &case_keyword, predicate, NULL, &parser->previous); - } - - // At this point we can create a case node, though we don't yet know if it - // is a case-in or case-when node. - yp_token_t end_keyword = not_provided(parser); - yp_case_node_t *case_node = yp_case_node_create(parser, &case_keyword, predicate, NULL, &end_keyword); - - if (match_type_p(parser, YP_TOKEN_KEYWORD_WHEN)) { - // At this point we've seen a when keyword, so we know this is a - // case-when node. We will continue to parse the when nodes until we hit - // the end of the list. - while (accept(parser, YP_TOKEN_KEYWORD_WHEN)) { - yp_token_t when_keyword = parser->previous; - yp_when_node_t *when_node = yp_when_node_create(parser, &when_keyword); - - do { - if (accept(parser, YP_TOKEN_USTAR)) { - yp_token_t operator = parser->previous; - yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after `*' operator."); - - yp_splat_node_t *splat_node = yp_splat_node_create(parser, &operator, expression); - yp_when_node_conditions_append(when_node, (yp_node_t *) splat_node); - - if (expression->type == YP_NODE_MISSING_NODE) break; - } else { - yp_node_t *condition = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after when keyword."); - yp_when_node_conditions_append(when_node, condition); - - if (condition->type == YP_NODE_MISSING_NODE) break; - } - } while (accept(parser, YP_TOKEN_COMMA)); - - if (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { - accept(parser, YP_TOKEN_KEYWORD_THEN); - } else { - expect(parser, YP_TOKEN_KEYWORD_THEN, "Expected a delimiter after the predicates of a `when' clause."); - } - - if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_WHEN, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_KEYWORD_END)) { - yp_when_node_statements_set(when_node, parse_statements(parser, YP_CONTEXT_CASE_WHEN)); - } - - yp_case_node_condition_append(case_node, (yp_node_t *) when_node); - } - } else { - // At this point we expect that we're parsing a case-in node. We will - // continue to parse the in nodes until we hit the end of the list. - while (match_type_p(parser, YP_TOKEN_KEYWORD_IN)) { - bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; - parser->pattern_matching_newlines = true; - - lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); - parser->command_start = false; - parser_lex(parser); - - yp_token_t in_keyword = parser->previous; - yp_node_t *pattern = parse_pattern(parser, true, "Expected a pattern after `in' keyword."); - parser->pattern_matching_newlines = previous_pattern_matching_newlines; - - // Since we're in the top-level of the case-in node we need to check - // for guard clauses in the form of `if` or `unless` statements. - if (accept(parser, YP_TOKEN_KEYWORD_IF_MODIFIER)) { - yp_token_t keyword = parser->previous; - yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after guard keyword."); - pattern = (yp_node_t *) yp_if_node_modifier_create(parser, pattern, &keyword, predicate); - } else if (accept(parser, YP_TOKEN_KEYWORD_UNLESS_MODIFIER)) { - yp_token_t keyword = parser->previous; - yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after guard keyword."); - pattern = (yp_node_t *) yp_unless_node_modifier_create(parser, pattern, &keyword, predicate); - } - - // Now we need to check for the terminator of the in node's pattern. - // It can be a newline or semicolon optionally followed by a `then` - // keyword. - yp_token_t then_keyword; - if (accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON)) { - if (accept(parser, YP_TOKEN_KEYWORD_THEN)) { - then_keyword = parser->previous; - } else { - then_keyword = not_provided(parser); - } - } else { - expect(parser, YP_TOKEN_KEYWORD_THEN, "Expected a delimiter after the predicates of an `in' clause."); - then_keyword = parser->previous; - } - - // Now we can actually parse the statements associated with the in - // node. - yp_statements_node_t *statements; - if (match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_IN, YP_TOKEN_KEYWORD_ELSE, YP_TOKEN_KEYWORD_END)) { - statements = NULL; - } else { - statements = parse_statements(parser, YP_CONTEXT_CASE_IN); - } - - // Now that we have the full pattern and statements, we can create the - // node and attach it to the case node. - yp_node_t *condition = (yp_node_t *) yp_in_node_create(parser, pattern, statements, &in_keyword, &then_keyword); - yp_case_node_condition_append(case_node, condition); - } - } - - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - if (accept(parser, YP_TOKEN_KEYWORD_ELSE)) { - if (case_node->conditions.size < 1) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected else without no when clauses in case statement."); - } - - yp_token_t else_keyword = parser->previous; - yp_else_node_t *else_node; - - if (!match_type_p(parser, YP_TOKEN_KEYWORD_END)) { - else_node = yp_else_node_create(parser, &else_keyword, parse_statements(parser, YP_CONTEXT_ELSE), &parser->current); - } else { - else_node = yp_else_node_create(parser, &else_keyword, NULL, &parser->current); - } - - yp_case_node_consequent_set(case_node, else_node); - } - - expect(parser, YP_TOKEN_KEYWORD_END, "Expected case statement to end with an end keyword."); - yp_case_node_end_keyword_loc_set(case_node, &parser->previous); - return (yp_node_t *) case_node; - } - case YP_TOKEN_KEYWORD_BEGIN: { - parser_lex(parser); - - yp_token_t begin_keyword = parser->previous; - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - yp_statements_node_t *begin_statements = NULL; - - if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { - yp_accepts_block_stack_push(parser, true); - begin_statements = parse_statements(parser, YP_CONTEXT_BEGIN); - yp_accepts_block_stack_pop(parser); - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - } - - yp_begin_node_t *begin_node = yp_begin_node_create(parser, &begin_keyword, begin_statements); - parse_rescues(parser, begin_node); - - expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `begin` statement."); - begin_node->base.location.end = parser->previous.end; - yp_begin_node_end_keyword_set(begin_node, &parser->previous); - - if ((begin_node->else_clause != NULL) && (begin_node->rescue_clause == NULL)) { - yp_diagnostic_list_append( - &parser->error_list, - begin_node->else_clause->base.location.start, - begin_node->else_clause->base.location.end, - "else without rescue is useless" - ); - } - - return (yp_node_t *) begin_node; - } - case YP_TOKEN_KEYWORD_BEGIN_UPCASE: { - parser_lex(parser); - yp_token_t keyword = parser->previous; - - expect(parser, YP_TOKEN_BRACE_LEFT, "Expected '{' after 'BEGIN'."); - yp_token_t opening = parser->previous; - yp_statements_node_t *statements = parse_statements(parser, YP_CONTEXT_PREEXE); - - expect(parser, YP_TOKEN_BRACE_RIGHT, "Expected '}' after 'BEGIN' statements."); - return (yp_node_t *) yp_pre_execution_node_create(parser, &keyword, &opening, statements, &parser->previous); - } - case YP_TOKEN_KEYWORD_BREAK: - case YP_TOKEN_KEYWORD_NEXT: - case YP_TOKEN_KEYWORD_RETURN: { - parser_lex(parser); - - yp_token_t keyword = parser->previous; - yp_arguments_node_t *arguments = NULL; - - if ( - token_begins_expression_p(parser->current.type) || - match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR) - ) { - yp_binding_power_t binding_power = yp_binding_powers[parser->current.type].left; - - if (binding_power == YP_BINDING_POWER_UNSET || binding_power >= YP_BINDING_POWER_RANGE) { - arguments = yp_arguments_node_create(parser); - parse_arguments(parser, arguments, false, YP_TOKEN_EOF); - } - } - - switch (keyword.type) { - case YP_TOKEN_KEYWORD_BREAK: - return (yp_node_t *) yp_break_node_create(parser, &keyword, arguments); - case YP_TOKEN_KEYWORD_NEXT: - return (yp_node_t *) yp_next_node_create(parser, &keyword, arguments); - case YP_TOKEN_KEYWORD_RETURN: { - if ( - (parser->current_context->context == YP_CONTEXT_CLASS) || - (parser->current_context->context == YP_CONTEXT_MODULE) - ) { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Invalid return in class/module body"); - } - return (yp_node_t *) yp_return_node_create(parser, &keyword, arguments); - } - default: - assert(false && "unreachable"); - return (yp_node_t *) yp_missing_node_create(parser, parser->previous.start, parser->previous.end); - } - } - case YP_TOKEN_KEYWORD_SUPER: { - parser_lex(parser); - - yp_token_t keyword = parser->previous; - yp_arguments_t arguments = yp_arguments(parser); - parse_arguments_list(parser, &arguments, true); - - if (arguments.opening.type == YP_TOKEN_NOT_PROVIDED && arguments.arguments == NULL) { - return (yp_node_t *) yp_forwarding_super_node_create(parser, &keyword, &arguments); - } - - return (yp_node_t *) yp_super_node_create(parser, &keyword, &arguments); - } - case YP_TOKEN_KEYWORD_YIELD: { - parser_lex(parser); - - yp_token_t keyword = parser->previous; - yp_arguments_t arguments = yp_arguments(parser); - parse_arguments_list(parser, &arguments, false); - - return (yp_node_t *) yp_yield_node_create(parser, &keyword, &arguments.opening, arguments.arguments, &arguments.closing); - } - case YP_TOKEN_KEYWORD_CLASS: { - parser_lex(parser); - yp_token_t class_keyword = parser->previous; - yp_do_loop_stack_push(parser, false); - - if (accept(parser, YP_TOKEN_LESS_LESS)) { - yp_token_t operator = parser->previous; - yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_NOT, "Expected to find an expression after `<<`."); - - yp_parser_scope_push(parser, true); - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - - yp_node_t *statements = NULL; - if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { - statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_SCLASS); - } - - if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { - assert(statements == NULL || statements->type == YP_NODE_STATEMENTS_NODE); - statements = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) statements); - } - - expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `class` statement."); - - yp_token_list_t locals = parser->current_scope->locals; - yp_parser_scope_pop(parser); - yp_do_loop_stack_pop(parser); - return (yp_node_t *) yp_singleton_class_node_create(parser, &locals, &class_keyword, &operator, expression, statements, &parser->previous); - } - - yp_node_t *name = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected to find a class name after `class`."); - yp_token_t inheritance_operator; - yp_node_t *superclass; - - if (match_type_p(parser, YP_TOKEN_LESS)) { - inheritance_operator = parser->current; - lex_state_set(parser, YP_LEX_STATE_BEG); - - parser->command_start = true; - parser_lex(parser); - - superclass = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected to find a superclass after `<`."); - } else { - inheritance_operator = not_provided(parser); - superclass = NULL; - } - - yp_parser_scope_push(parser, true); - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - yp_node_t *statements = NULL; - - if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { - yp_accepts_block_stack_push(parser, true); - statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_CLASS); - yp_accepts_block_stack_pop(parser); - } - - if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { - assert(statements == NULL || statements->type == YP_NODE_STATEMENTS_NODE); - statements = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) statements); - } - - expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `class` statement."); - - if (context_def_p(parser)) { - yp_diagnostic_list_append(&parser->error_list, class_keyword.start, class_keyword.end, "Class definition in method body"); - } - - yp_token_list_t locals = parser->current_scope->locals; - yp_parser_scope_pop(parser); - yp_do_loop_stack_pop(parser); - return (yp_node_t *) yp_class_node_create(parser, &locals, &class_keyword, name, &inheritance_operator, superclass, statements, &parser->previous); - } - case YP_TOKEN_KEYWORD_DEF: { - yp_token_t def_keyword = parser->current; - - yp_node_t *receiver = NULL; - yp_token_t operator = not_provided(parser); - yp_token_t name = not_provided(parser); - - context_push(parser, YP_CONTEXT_DEF_PARAMS); - parser_lex(parser); - - switch (parser->current.type) { - case YP_CASE_OPERATOR: - yp_parser_scope_push(parser, true); - lex_state_set(parser, YP_LEX_STATE_ENDFN); - parser_lex(parser); - name = parser->previous; - break; - case YP_TOKEN_IDENTIFIER: { - yp_parser_scope_push(parser, true); - parser_lex(parser); - - if (match_any_type_p(parser, 2, YP_TOKEN_DOT, YP_TOKEN_COLON_COLON)) { - receiver = parse_vcall(parser); - - lex_state_set(parser, YP_LEX_STATE_FNAME); - parser_lex(parser); - - operator = parser->previous; - name = parse_method_definition_name(parser); - - if (name.type == YP_TOKEN_MISSING) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Expected a method name after receiver."); - } - } else { - name = parser->previous; - } - - break; - } - case YP_TOKEN_CONSTANT: - case YP_TOKEN_INSTANCE_VARIABLE: - case YP_TOKEN_CLASS_VARIABLE: - case YP_TOKEN_GLOBAL_VARIABLE: - case YP_TOKEN_KEYWORD_NIL: - case YP_TOKEN_KEYWORD_SELF: - case YP_TOKEN_KEYWORD_TRUE: - case YP_TOKEN_KEYWORD_FALSE: - case YP_TOKEN_KEYWORD___FILE__: - case YP_TOKEN_KEYWORD___LINE__: - case YP_TOKEN_KEYWORD___ENCODING__: { - yp_parser_scope_push(parser, true); - parser_lex(parser); - yp_token_t identifier = parser->previous; - - if (match_any_type_p(parser, 2, YP_TOKEN_DOT, YP_TOKEN_COLON_COLON)) { - lex_state_set(parser, YP_LEX_STATE_FNAME); - parser_lex(parser); - operator = parser->previous; - - switch (identifier.type) { - case YP_TOKEN_CONSTANT: - receiver = (yp_node_t *) yp_constant_read_node_create(parser, &identifier); - break; - case YP_TOKEN_INSTANCE_VARIABLE: - receiver = (yp_node_t *) yp_instance_variable_read_node_create(parser, &identifier); - break; - case YP_TOKEN_CLASS_VARIABLE: - receiver = (yp_node_t *) yp_class_variable_read_node_create(parser, &identifier); - break; - case YP_TOKEN_GLOBAL_VARIABLE: - receiver = (yp_node_t *) yp_global_variable_read_node_create(parser, &identifier); - break; - case YP_TOKEN_KEYWORD_NIL: - receiver = (yp_node_t *) yp_nil_node_create(parser, &identifier); - break; - case YP_TOKEN_KEYWORD_SELF: - receiver = (yp_node_t *) yp_self_node_create(parser, &identifier); - break; - case YP_TOKEN_KEYWORD_TRUE: - receiver = (yp_node_t *) yp_true_node_create(parser, &identifier); - break; - case YP_TOKEN_KEYWORD_FALSE: - receiver = (yp_node_t *)yp_false_node_create(parser, &identifier); - break; - case YP_TOKEN_KEYWORD___FILE__: - receiver = (yp_node_t *) yp_source_file_node_create(parser, &identifier); - break; - case YP_TOKEN_KEYWORD___LINE__: - receiver = (yp_node_t *) yp_source_line_node_create(parser, &identifier); - break; - case YP_TOKEN_KEYWORD___ENCODING__: - receiver = (yp_node_t *) yp_source_encoding_node_create(parser, &identifier); - break; - default: - break; - } - - name = parse_method_definition_name(parser); - if (name.type == YP_TOKEN_MISSING) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Expected a method name after receiver."); - } - } else { - name = identifier; - } - break; - } - case YP_TOKEN_PARENTHESIS_LEFT: { - parser_lex(parser); - yp_token_t lparen = parser->previous; - yp_node_t *expression = parse_expression(parser, YP_BINDING_POWER_STATEMENT, "Expected to be able to parse receiver."); - - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected closing ')' for receiver."); - yp_token_t rparen = parser->previous; - - lex_state_set(parser, YP_LEX_STATE_FNAME); - expect_any(parser, "Expected '.' or '::' after receiver", 2, YP_TOKEN_DOT, YP_TOKEN_COLON_COLON); - - operator = parser->previous; - receiver = (yp_node_t *) yp_parentheses_node_create(parser, &lparen, expression, &rparen); - - yp_parser_scope_push(parser, true); - name = parse_method_definition_name(parser); - break; - } - default: - yp_parser_scope_push(parser, true); - name = parse_method_definition_name(parser); - - if (name.type == YP_TOKEN_MISSING) { - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Expected a method name after receiver."); - } - break; - } - - yp_token_t lparen; - yp_token_t rparen; - yp_parameters_node_t *params; - - switch (parser->current.type) { - case YP_TOKEN_PARENTHESIS_LEFT: { - parser_lex(parser); - lparen = parser->previous; - - if (match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { - params = NULL; - } else { - params = parse_parameters(parser, YP_BINDING_POWER_DEFINED, true, false, true); - } - - lex_state_set(parser, YP_LEX_STATE_BEG); - parser->command_start = true; - - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' after left parenthesis."); - rparen = parser->previous; - break; - } - case YP_CASE_PARAMETER: { - lparen = not_provided(parser); - rparen = not_provided(parser); - params = parse_parameters(parser, YP_BINDING_POWER_DEFINED, false, false, true); - break; - } - default: { - lparen = not_provided(parser); - rparen = not_provided(parser); - params = NULL; - break; - } - } - - context_pop(parser); - yp_node_t *statements = NULL; - yp_token_t equal; - yp_token_t end_keyword; - - if (accept(parser, YP_TOKEN_EQUAL)) { - if (token_is_setter_name(&name)) { - yp_diagnostic_list_append(&parser->error_list, name.start, name.end, "Setter method cannot be defined in an endless method definition"); - } - equal = parser->previous; - - context_push(parser, YP_CONTEXT_DEF); - statements = (yp_node_t *) yp_statements_node_create(parser); - - yp_node_t *statement = parse_expression(parser, YP_BINDING_POWER_ASSIGNMENT + 1, "Expected to be able to parse body of endless method definition."); - - if (accept(parser, YP_TOKEN_KEYWORD_RESCUE_MODIFIER)) { - yp_token_t rescue_keyword = parser->previous; - yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the rescue keyword."); - yp_rescue_modifier_node_t *rescue_node = yp_rescue_modifier_node_create(parser, statement, &rescue_keyword, value); - statement = (yp_node_t *)rescue_node; - } - - yp_statements_node_body_append((yp_statements_node_t *) statements, statement); - context_pop(parser); - end_keyword = not_provided(parser); - } else { - equal = not_provided(parser); - - if (lparen.type == YP_TOKEN_NOT_PROVIDED) { - lex_state_set(parser, YP_LEX_STATE_BEG); - parser->command_start = true; - expect_any(parser, "Expected a terminator after the parameters", 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - } else { - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - } - - yp_accepts_block_stack_push(parser, true); - yp_do_loop_stack_push(parser, false); - - if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { - statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_DEF); - } - - if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { - assert(statements == NULL || statements->type == YP_NODE_STATEMENTS_NODE); - statements = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) statements); - } - - yp_accepts_block_stack_pop(parser); - yp_do_loop_stack_pop(parser); - expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `def` statement."); - end_keyword = parser->previous; - } - - yp_token_list_t locals = parser->current_scope->locals; - yp_parser_scope_pop(parser); - - return (yp_node_t *) yp_def_node_create( - parser, - &name, - receiver, - params, - statements, - &locals, - &def_keyword, - &operator, - &lparen, - &rparen, - &equal, - &end_keyword - ); - } - case YP_TOKEN_KEYWORD_DEFINED: { - parser_lex(parser); - yp_token_t keyword = parser->previous; - - yp_token_t lparen; - yp_token_t rparen; - yp_node_t *expression; - - if (accept(parser, YP_TOKEN_PARENTHESIS_LEFT)) { - lparen = parser->previous; - expression = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected expression after `defined?`."); - - if (parser->recovering) { - rparen = not_provided(parser); - } else { - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' after 'defined?' expression."); - rparen = parser->previous; - } - } else { - lparen = not_provided(parser); - rparen = not_provided(parser); - expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected expression after `defined?`."); - } - - return (yp_node_t *) yp_defined_node_create( - parser, - &lparen, - expression, - &rparen, - &(yp_location_t) { .start = keyword.start, .end = keyword.end } - ); - } - case YP_TOKEN_KEYWORD_END_UPCASE: { - parser_lex(parser); - yp_token_t keyword = parser->previous; - - expect(parser, YP_TOKEN_BRACE_LEFT, "Expected '{' after 'END'."); - yp_token_t opening = parser->previous; - yp_statements_node_t *statements = parse_statements(parser, YP_CONTEXT_POSTEXE); - - expect(parser, YP_TOKEN_BRACE_RIGHT, "Expected '}' after 'END' statements."); - return (yp_node_t *) yp_post_execution_node_create(parser, &keyword, &opening, statements, &parser->previous); - } - case YP_TOKEN_KEYWORD_FALSE: - parser_lex(parser); - return (yp_node_t *)yp_false_node_create(parser, &parser->previous); - case YP_TOKEN_KEYWORD_FOR: { - parser_lex(parser); - yp_token_t for_keyword = parser->previous; - - yp_node_t *index = parse_targets(parser, NULL, YP_BINDING_POWER_INDEX); - yp_do_loop_stack_push(parser, true); - - expect(parser, YP_TOKEN_KEYWORD_IN, "Expected keyword in."); - yp_token_t in_keyword = parser->previous; - - yp_node_t *collection = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected collection."); - yp_do_loop_stack_pop(parser); - - yp_token_t do_keyword; - if (accept(parser, YP_TOKEN_KEYWORD_DO_LOOP)) { - do_keyword = parser->previous; - } else { - do_keyword = not_provided(parser); - } - - accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE); - yp_statements_node_t *statements = NULL; - - if (!accept(parser, YP_TOKEN_KEYWORD_END)) { - statements = parse_statements(parser, YP_CONTEXT_FOR); - expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close for loop."); - } - - return (yp_node_t *) yp_for_node_create(parser, index, collection, statements, &for_keyword, &in_keyword, &do_keyword, &parser->previous); - } - case YP_TOKEN_KEYWORD_IF: - parser_lex(parser); - return parse_conditional(parser, YP_CONTEXT_IF); - case YP_TOKEN_KEYWORD_UNDEF: { - parser_lex(parser); - yp_undef_node_t *undef = yp_undef_node_create(parser, &parser->previous); - yp_node_t *name = parse_undef_argument(parser); - - if (name->type != YP_NODE_MISSING_NODE) { - yp_undef_node_append(undef, name); - - while (match_type_p(parser, YP_TOKEN_COMMA)) { - lex_state_set(parser, YP_LEX_STATE_FNAME | YP_LEX_STATE_FITEM); - parser_lex(parser); - name = parse_undef_argument(parser); - if (name->type == YP_NODE_MISSING_NODE) break; - - yp_undef_node_append(undef, name); - } - } else { - yp_node_destroy(parser, name); - } - - return (yp_node_t *) undef; - } - case YP_TOKEN_KEYWORD_NOT: { - parser_lex(parser); - - yp_token_t message = parser->previous; - yp_arguments_t arguments = yp_arguments(parser); - yp_node_t *receiver = NULL; - - accept(parser, YP_TOKEN_NEWLINE); - - if (accept(parser, YP_TOKEN_PARENTHESIS_LEFT)) { - arguments.opening = parser->previous; - - if (accept(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { - arguments.closing = parser->previous; - } else { - receiver = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected expression after `not`."); - - if (!parser->recovering) { - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' after 'not' expression."); - arguments.closing = parser->previous; - } - } - } else { - receiver = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected expression after `not`."); - } - - return (yp_node_t *) yp_call_node_not_create(parser, receiver, &message, &arguments); - } - case YP_TOKEN_KEYWORD_UNLESS: - parser_lex(parser); - return parse_conditional(parser, YP_CONTEXT_UNLESS); - case YP_TOKEN_KEYWORD_MODULE: { - parser_lex(parser); - - yp_token_t module_keyword = parser->previous; - yp_node_t *name = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected to find a module name after `module`."); - - // If we can recover from a syntax error that occurred while parsing the - // name of the module, then we'll handle that here. - if (name->type == YP_NODE_MISSING_NODE) { - yp_token_list_t locals = YP_EMPTY_TOKEN_LIST; - yp_token_t end_keyword = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; - return (yp_node_t *) yp_module_node_create(parser, &locals, &module_keyword, name, NULL, &end_keyword); - } - - while (accept(parser, YP_TOKEN_COLON_COLON)) { - yp_token_t double_colon = parser->previous; - - expect(parser, YP_TOKEN_CONSTANT, "Expected to find a module name after `::`."); - yp_node_t *constant = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); - - name = (yp_node_t *)yp_constant_path_node_create(parser, name, &double_colon, constant); - } - - yp_parser_scope_push(parser, true); - accept_any(parser, 2, YP_TOKEN_SEMICOLON, YP_TOKEN_NEWLINE); - yp_node_t *statements = NULL; - - if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE, YP_TOKEN_KEYWORD_END)) { - yp_accepts_block_stack_push(parser, true); - statements = (yp_node_t *) parse_statements(parser, YP_CONTEXT_MODULE); - yp_accepts_block_stack_pop(parser); - } - - if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { - assert(statements == NULL || statements->type == YP_NODE_STATEMENTS_NODE); - statements = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) statements); - } - - yp_token_list_t locals = parser->current_scope->locals; - yp_parser_scope_pop(parser); - - expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `module` statement."); - - if (context_def_p(parser)) { - yp_diagnostic_list_append(&parser->error_list, module_keyword.start, module_keyword.end, "Module definition in method body"); - } - - return (yp_node_t *) yp_module_node_create(parser, &locals, &module_keyword, name, statements, &parser->previous); - } - case YP_TOKEN_KEYWORD_NIL: - parser_lex(parser); - return (yp_node_t *) yp_nil_node_create(parser, &parser->previous); - case YP_TOKEN_KEYWORD_REDO: - parser_lex(parser); - return (yp_node_t *) yp_redo_node_create(parser, &parser->previous); - case YP_TOKEN_KEYWORD_RETRY: - parser_lex(parser); - return (yp_node_t *) yp_retry_node_create(parser, &parser->previous); - case YP_TOKEN_KEYWORD_SELF: - parser_lex(parser); - return (yp_node_t *) yp_self_node_create(parser, &parser->previous); - case YP_TOKEN_KEYWORD_TRUE: - parser_lex(parser); - return (yp_node_t *) yp_true_node_create(parser, &parser->previous); - case YP_TOKEN_KEYWORD_UNTIL: { - yp_do_loop_stack_push(parser, true); - parser_lex(parser); - yp_token_t keyword = parser->previous; - - yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected predicate expression after `until`."); - yp_do_loop_stack_pop(parser); - - accept_any(parser, 3, YP_TOKEN_KEYWORD_DO_LOOP, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - yp_statements_node_t *statements = NULL; - - if (!accept(parser, YP_TOKEN_KEYWORD_END)) { - yp_accepts_block_stack_push(parser, true); - statements = parse_statements(parser, YP_CONTEXT_UNTIL); - yp_accepts_block_stack_pop(parser); - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `until` statement."); - } - - yp_until_node_t *until_node = yp_until_node_create(parser, &keyword, predicate, statements); - if (parser->previous.type == YP_TOKEN_KEYWORD_END) { - until_node->base.location.end = parser->previous.end; - } - - return (yp_node_t *) until_node; - } - case YP_TOKEN_KEYWORD_WHILE: { - yp_do_loop_stack_push(parser, true); - parser_lex(parser); - yp_token_t keyword = parser->previous; - - yp_node_t *predicate = parse_expression(parser, YP_BINDING_POWER_COMPOSITION, "Expected predicate expression after `while`."); - yp_do_loop_stack_pop(parser); - - accept_any(parser, 3, YP_TOKEN_KEYWORD_DO_LOOP, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - yp_statements_node_t *statements = NULL; - - if (!accept(parser, YP_TOKEN_KEYWORD_END)) { - yp_accepts_block_stack_push(parser, true); - statements = parse_statements(parser, YP_CONTEXT_WHILE); - yp_accepts_block_stack_pop(parser); - accept_any(parser, 2, YP_TOKEN_NEWLINE, YP_TOKEN_SEMICOLON); - expect(parser, YP_TOKEN_KEYWORD_END, "Expected `end` to close `while` statement."); - } - - yp_while_node_t *while_node = yp_while_node_create(parser, &keyword, predicate, statements); - if (parser->previous.type == YP_TOKEN_KEYWORD_END) { - while_node->base.location.end = parser->previous.end; - } - return (yp_node_t *) while_node; - } - case YP_TOKEN_PERCENT_LOWER_I: { - parser_lex(parser); - yp_token_t opening = parser->previous; - yp_array_node_t *array = yp_array_node_create(parser, &opening, &opening); - - while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { - if (yp_array_node_size(array) == 0) { - accept(parser, YP_TOKEN_WORDS_SEP); - } else { - expect(parser, YP_TOKEN_WORDS_SEP, "Expected a separator for the symbols in a `%i` list."); - if (match_type_p(parser, YP_TOKEN_STRING_END)) break; - } - - if (match_type_p(parser, YP_TOKEN_STRING_END)) break; - expect(parser, YP_TOKEN_STRING_CONTENT, "Expected a symbol in a `%i` list."); - - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - - yp_node_t *symbol = (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &parser->previous, &closing); - yp_array_node_elements_append(array, symbol); - } - - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a `%i` list."); - yp_array_node_close_set(array, &parser->previous); - - return (yp_node_t *) array; - } - case YP_TOKEN_PERCENT_UPPER_I: { - parser_lex(parser); - yp_token_t opening = parser->previous; - yp_array_node_t *array = yp_array_node_create(parser, &opening, &opening); - - // This is the current node that we are parsing that will be added to the - // list of elements. - yp_node_t *current = NULL; - - while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { - switch (parser->current.type) { - case YP_TOKEN_WORDS_SEP: { - if (current == NULL) { - // If we hit a separator before we have any content, then we don't - // need to do anything. - } else { - // If we hit a separator after we've hit content, then we need to - // append that content to the list and reset the current node. - yp_array_node_elements_append(array, current); - current = NULL; - } - - parser_lex(parser); - break; - } - case YP_TOKEN_STRING_CONTENT: { - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - - if (current == NULL) { - // If we hit content and the current node is NULL, then this is - // the first string content we've seen. In that case we're going - // to create a new string node and set that to the current. - parser_lex(parser); - current = (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &parser->previous, &closing); - } else if (current->type == YP_NODE_INTERPOLATED_SYMBOL_NODE) { - // If we hit string content and the current node is an - // interpolated string, then we need to append the string content - // to the list of child nodes. - yp_node_t *part = parse_string_part(parser); - yp_interpolated_symbol_node_append((yp_interpolated_symbol_node_t *) current, part); - } else { - assert(false && "unreachable"); - } - - break; - } - case YP_TOKEN_EMBVAR: { - if (current == NULL) { - // If we hit an embedded variable and the current node is NULL, - // then this is the start of a new string. We'll set the current - // node to a new interpolated string. - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - current = (yp_node_t *) yp_interpolated_symbol_node_create(parser, &opening, NULL, &closing); - } else if (current->type == YP_NODE_SYMBOL_NODE) { - // If we hit an embedded variable and the current node is a string - // node, then we'll convert the current into an interpolated - // string and add the string node to the list of parts. - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - yp_interpolated_symbol_node_t *interpolated = yp_interpolated_symbol_node_create(parser, &opening, NULL, &closing); - - current = (yp_node_t *) yp_symbol_node_to_string_node(parser, (yp_symbol_node_t *) current); - yp_interpolated_symbol_node_append(interpolated, current); - current = (yp_node_t *) interpolated; - } else { - // If we hit an embedded variable and the current node is an - // interpolated string, then we'll just add the embedded variable. - } - - yp_node_t *part = parse_string_part(parser); - yp_interpolated_symbol_node_append((yp_interpolated_symbol_node_t *) current, part); - break; - } - case YP_TOKEN_EMBEXPR_BEGIN: { - if (current == NULL) { - // If we hit an embedded expression and the current node is NULL, - // then this is the start of a new string. We'll set the current - // node to a new interpolated string. - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - current = (yp_node_t *) yp_interpolated_symbol_node_create(parser, &opening, NULL, &closing); - } else if (current->type == YP_NODE_SYMBOL_NODE) { - // If we hit an embedded expression and the current node is a - // string node, then we'll convert the current into an - // interpolated string and add the string node to the list of - // parts. - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - yp_interpolated_symbol_node_t *interpolated = yp_interpolated_symbol_node_create(parser, &opening, NULL, &closing); - - current = (yp_node_t *) yp_symbol_node_to_string_node(parser, (yp_symbol_node_t *) current); - yp_interpolated_symbol_node_append(interpolated, current); - current = (yp_node_t *) interpolated; - } else if (current->type == YP_NODE_INTERPOLATED_SYMBOL_NODE) { - // If we hit an embedded expression and the current node is an - // interpolated string, then we'll just continue on. - } else { - assert(false && "unreachable"); - } - - yp_node_t *part = parse_string_part(parser); - yp_interpolated_symbol_node_append((yp_interpolated_symbol_node_t *) current, part); - break; - } - default: - expect(parser, YP_TOKEN_STRING_CONTENT, "Expected a symbol in a `%I` list."); - parser_lex(parser); - break; - } - } - - // If we have a current node, then we need to append it to the list. - if (current) { - yp_array_node_elements_append(array, current); - } - - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a `%I` list."); - yp_array_node_close_set(array, &parser->previous); - - return (yp_node_t *) array; - } - case YP_TOKEN_PERCENT_LOWER_W: { - parser_lex(parser); - yp_token_t opening = parser->previous; - yp_array_node_t *array = yp_array_node_create(parser, &opening, &opening); - - // skip all leading whitespaces - accept(parser, YP_TOKEN_WORDS_SEP); - - while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { - if (yp_array_node_size(array) == 0) { - accept(parser, YP_TOKEN_WORDS_SEP); - } else { - expect(parser, YP_TOKEN_WORDS_SEP, "Expected a separator for the strings in a `%w` list."); - if (match_type_p(parser, YP_TOKEN_STRING_END)) break; - } - expect(parser, YP_TOKEN_STRING_CONTENT, "Expected a string in a `%w` list."); - - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - yp_node_t *string = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &parser->previous, &closing, YP_UNESCAPE_ALL); - yp_array_node_elements_append(array, string); - } - - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a `%w` list."); - yp_array_node_close_set(array, &parser->previous); - - return (yp_node_t *) array; - } - case YP_TOKEN_PERCENT_UPPER_W: { - parser_lex(parser); - yp_token_t opening = parser->previous; - yp_array_node_t *array = yp_array_node_create(parser, &opening, &opening); - - // This is the current node that we are parsing that will be added to the - // list of elements. - yp_node_t *current = NULL; - - while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { - switch (parser->current.type) { - case YP_TOKEN_WORDS_SEP: { - if (current == NULL) { - // If we hit a separator before we have any content, then we don't - // need to do anything. - } else { - // If we hit a separator after we've hit content, then we need to - // append that content to the list and reset the current node. - yp_array_node_elements_append(array, current); - current = NULL; - } - - parser_lex(parser); - break; - } - case YP_TOKEN_STRING_CONTENT: { - if (current == NULL) { - // If we hit content and the current node is NULL, then this is - // the first string content we've seen. In that case we're going - // to create a new string node and set that to the current. - current = parse_string_part(parser); - } else if (current->type == YP_NODE_INTERPOLATED_STRING_NODE) { - // If we hit string content and the current node is an - // interpolated string, then we need to append the string content - // to the list of child nodes. - yp_node_t *part = parse_string_part(parser); - yp_interpolated_string_node_append((yp_interpolated_string_node_t *) current, part); - } else { - assert(false && "unreachable"); - } - - break; - } - case YP_TOKEN_EMBVAR: { - if (current == NULL) { - // If we hit an embedded variable and the current node is NULL, - // then this is the start of a new string. We'll set the current - // node to a new interpolated string. - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - current = (yp_node_t *) yp_interpolated_string_node_create(parser, &opening, NULL, &closing); - } else if (current->type == YP_NODE_STRING_NODE) { - // If we hit an embedded variable and the current node is a string - // node, then we'll convert the current into an interpolated - // string and add the string node to the list of parts. - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - yp_interpolated_string_node_t *interpolated = yp_interpolated_string_node_create(parser, &opening, NULL, &closing); - yp_interpolated_string_node_append(interpolated, current); - current = (yp_node_t *) interpolated; - } else { - // If we hit an embedded variable and the current node is an - // interpolated string, then we'll just add the embedded variable. - } - - yp_node_t *part = parse_string_part(parser); - yp_interpolated_string_node_append((yp_interpolated_string_node_t *) current, part); - break; - } - case YP_TOKEN_EMBEXPR_BEGIN: { - if (current == NULL) { - // If we hit an embedded expression and the current node is NULL, - // then this is the start of a new string. We'll set the current - // node to a new interpolated string. - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - current = (yp_node_t *) yp_interpolated_string_node_create(parser, &opening, NULL, &closing); - } else if (current->type == YP_NODE_STRING_NODE) { - // If we hit an embedded expression and the current node is a - // string node, then we'll convert the current into an - // interpolated string and add the string node to the list of - // parts. - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - yp_interpolated_string_node_t *interpolated = yp_interpolated_string_node_create(parser, &opening, NULL, &closing); - yp_interpolated_string_node_append(interpolated, current); - current = (yp_node_t *) interpolated; - } else if (current->type == YP_NODE_INTERPOLATED_STRING_NODE) { - // If we hit an embedded expression and the current node is an - // interpolated string, then we'll just continue on. - } else { - assert(false && "unreachable"); - } - - yp_node_t *part = parse_string_part(parser); - yp_interpolated_string_node_append((yp_interpolated_string_node_t *) current, part); - break; - } - default: - expect(parser, YP_TOKEN_STRING_CONTENT, "Expected a string in a `%W` list."); - parser_lex(parser); - break; - } - } - - // If we have a current node, then we need to append it to the list. - if (current) { - yp_array_node_elements_append(array, current); - } - - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a `%W` list."); - yp_array_node_close_set(array, &parser->previous); - - return (yp_node_t *) array; - } - case YP_TOKEN_RATIONAL_NUMBER: - parser_lex(parser); - return (yp_node_t *) yp_rational_node_create(parser, &parser->previous); - case YP_TOKEN_REGEXP_BEGIN: { - yp_token_t opening = parser->current; - parser_lex(parser); - - if (match_type_p(parser, YP_TOKEN_REGEXP_END)) { - // If we get here, then we have an end immediately after a start. In - // that case we'll create an empty content token and return an - // uninterpolated regular expression. - yp_token_t content = (yp_token_t) { - .type = YP_TOKEN_STRING_CONTENT, - .start = parser->previous.end, - .end = parser->previous.end - }; - - parser_lex(parser); - return (yp_node_t *) yp_regular_expression_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_ALL); - } - - yp_interpolated_regular_expression_node_t *node; - - if (match_type_p(parser, YP_TOKEN_STRING_CONTENT)) { - // In this case we've hit string content so we know the regular - // expression at least has something in it. We'll need to check if the - // following token is the end (in which case we can return a plain - // regular expression) or if it's not then it has interpolation. - yp_token_t content = parser->current; - parser_lex(parser); - - // If we hit an end, then we can create a regular expression node - // without interpolation, which can be represented more succinctly and - // more easily compiled. - if (accept(parser, YP_TOKEN_REGEXP_END)) { - return (yp_node_t *) yp_regular_expression_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_ALL); - } - - // If we get here, then we have interpolation so we'll need to create - // a regular expression node with interpolation. - node = yp_interpolated_regular_expression_node_create(parser, &opening, &opening); - - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - yp_node_t *part = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &parser->previous, &closing, YP_UNESCAPE_ALL); - yp_interpolated_regular_expression_node_append(node, part); - } else { - // If the first part of the body of the regular expression is not a - // string content, then we have interpolation and we need to create an - // interpolated regular expression node. - node = yp_interpolated_regular_expression_node_create(parser, &opening, &opening); - } - - // Now that we're here and we have interpolation, we'll parse all of the - // parts into the list. - while (!match_any_type_p(parser, 2, YP_TOKEN_REGEXP_END, YP_TOKEN_EOF)) { - yp_node_t *part = parse_string_part(parser); - if (part != NULL) { - yp_interpolated_regular_expression_node_append(node, part); - } - } - - expect(parser, YP_TOKEN_REGEXP_END, "Expected a closing delimiter for a regular expression."); - yp_interpolated_regular_expression_node_closing_set(node, &parser->previous); - - return (yp_node_t *) node; - } - case YP_TOKEN_BACKTICK: - case YP_TOKEN_PERCENT_LOWER_X: { - parser_lex(parser); - yp_token_t opening = parser->previous; - - // When we get here, we don't know if this string is going to have - // interpolation or not, even though it is allowed. Still, we want to be - // able to return a string node without interpolation if we can since - // it'll be faster. - if (match_type_p(parser, YP_TOKEN_STRING_END)) { - // If we get here, then we have an end immediately after a start. In - // that case we'll create an empty content token and return an - // uninterpolated string. - yp_token_t content = (yp_token_t) { - .type = YP_TOKEN_STRING_CONTENT, - .start = parser->previous.end, - .end = parser->previous.end - }; - - parser_lex(parser); - return (yp_node_t *) yp_xstring_node_create(parser, &opening, &content, &parser->previous); - } - - yp_interpolated_x_string_node_t *node; - - if (match_type_p(parser, YP_TOKEN_STRING_CONTENT)) { - // In this case we've hit string content so we know the string at least - // has something in it. We'll need to check if the following token is - // the end (in which case we can return a plain string) or if it's not - // then it has interpolation. - yp_token_t content = parser->current; - parser_lex(parser); - - if (accept(parser, YP_TOKEN_STRING_END)) { - return (yp_node_t *) yp_xstring_node_create_and_unescape(parser, &opening, &content, &parser->previous); - } - - // If we get here, then we have interpolation so we'll need to create - // a string node with interpolation. - node = yp_interpolated_xstring_node_create(parser, &opening, &opening); - - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - yp_node_t *part = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &parser->previous, &closing, YP_UNESCAPE_ALL); - yp_interpolated_xstring_node_append(node, part); - } else { - // If the first part of the body of the string is not a string content, - // then we have interpolation and we need to create an interpolated - // string node. - node = yp_interpolated_xstring_node_create(parser, &opening, &opening); - } - - while (!match_any_type_p(parser, 2, YP_TOKEN_STRING_END, YP_TOKEN_EOF)) { - yp_node_t *part = parse_string_part(parser); - if (part != NULL) { - yp_interpolated_xstring_node_append(node, part); - } - } - - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for an xstring."); - yp_interpolated_xstring_node_closing_set(node, &parser->previous); - return (yp_node_t *) node; - } - case YP_TOKEN_USTAR: { - parser_lex(parser); - - // * operators at the beginning of expressions are only valid in the - // context of a multiple assignment. We enforce that here. We'll still lex - // past it though and create a missing node place. - if (binding_power != YP_BINDING_POWER_STATEMENT) { - return (yp_node_t *) yp_missing_node_create(parser, parser->previous.start, parser->previous.end); - } - - yp_token_t operator = parser->previous; - yp_node_t *name = NULL; - - if (token_begins_expression_p(parser->current.type)) { - name = parse_expression(parser, YP_BINDING_POWER_INDEX, "Expected an expression after '*'."); - } - - yp_node_t *splat = (yp_node_t *) yp_splat_node_create(parser, &operator, name); - return parse_targets(parser, splat, YP_BINDING_POWER_INDEX); - } - case YP_TOKEN_BANG: { - parser_lex(parser); - - yp_token_t operator = parser->previous; - yp_node_t *receiver = parse_expression(parser, yp_binding_powers[parser->previous.type].right, "Expected a receiver after unary !."); - yp_call_node_t *node = yp_call_node_unary_create(parser, &operator, receiver, "!"); - - return (yp_node_t *) node; - } - case YP_TOKEN_TILDE: { - parser_lex(parser); - - yp_token_t operator = parser->previous; - yp_node_t *receiver = parse_expression(parser, yp_binding_powers[parser->previous.type].right, "Expected a receiver after unary ~."); - yp_call_node_t *node = yp_call_node_unary_create(parser, &operator, receiver, "~"); - - return (yp_node_t *) node; - } - case YP_TOKEN_UMINUS: - case YP_TOKEN_UMINUS_NUM: { - parser_lex(parser); - - yp_token_t operator = parser->previous; - yp_node_t *receiver = parse_expression(parser, yp_binding_powers[parser->previous.type].right, "Expected a receiver after unary -."); - yp_call_node_t *node = yp_call_node_unary_create(parser, &operator, receiver, "-@"); - - return (yp_node_t *) node; - } - case YP_TOKEN_MINUS_GREATER: { - int previous_lambda_enclosure_nesting = parser->lambda_enclosure_nesting; - parser->lambda_enclosure_nesting = parser->enclosure_nesting; - - yp_accepts_block_stack_push(parser, true); - parser_lex(parser); - - yp_token_t opening = parser->previous; - yp_parser_scope_push(parser, false); - yp_block_parameters_node_t *params; - - switch (parser->current.type) { - case YP_TOKEN_PARENTHESIS_LEFT: { - yp_token_t block_parameters_opening = parser->current; - parser_lex(parser); - - if (match_type_p(parser, YP_TOKEN_PARENTHESIS_RIGHT)) { - params = yp_block_parameters_node_create(parser, NULL, &block_parameters_opening); - } else { - params = parse_block_parameters(parser, false, &block_parameters_opening, true); - } - - accept(parser, YP_TOKEN_NEWLINE); - expect(parser, YP_TOKEN_PARENTHESIS_RIGHT, "Expected ')' after left parenthesis."); - yp_block_parameters_node_closing_set(params, &parser->previous); - - break; - } - case YP_CASE_PARAMETER: { - yp_token_t opening = not_provided(parser); - params = parse_block_parameters(parser, false, &opening, true); - break; - } - default: { - params = NULL; - break; - } - } - - yp_node_t *body = NULL; - parser->lambda_enclosure_nesting = previous_lambda_enclosure_nesting; - - if (accept(parser, YP_TOKEN_LAMBDA_BEGIN)) { - if (!accept(parser, YP_TOKEN_BRACE_RIGHT)) { - body = (yp_node_t *) parse_statements(parser, YP_CONTEXT_LAMBDA_BRACES); - expect(parser, YP_TOKEN_BRACE_RIGHT, "Expecting '}' to close lambda block."); - } - } else { - expect(parser, YP_TOKEN_KEYWORD_DO, "Expected a 'do' keyword or a '{' to open lambda block."); - - if (!match_any_type_p(parser, 3, YP_TOKEN_KEYWORD_END, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { - body = (yp_node_t *) parse_statements(parser, YP_CONTEXT_LAMBDA_DO_END); - } - - if (match_any_type_p(parser, 2, YP_TOKEN_KEYWORD_RESCUE, YP_TOKEN_KEYWORD_ENSURE)) { - assert(body == NULL || body->type == YP_NODE_STATEMENTS_NODE); - body = (yp_node_t *) parse_rescues_as_begin(parser, (yp_statements_node_t *) body); - } - - expect(parser, YP_TOKEN_KEYWORD_END, "Expecting 'end' keyword to close lambda block."); - } - - yp_token_list_t locals = parser->current_scope->locals; - yp_parser_scope_pop(parser); - yp_accepts_block_stack_pop(parser); - return (yp_node_t *) yp_lambda_node_create(parser, &locals, &opening, params, body); - } - case YP_TOKEN_UPLUS: { - parser_lex(parser); - - yp_token_t operator = parser->previous; - yp_node_t *receiver = parse_expression(parser, yp_binding_powers[parser->previous.type].right, "Expected a receiver after unary +."); - yp_call_node_t *node = yp_call_node_unary_create(parser, &operator, receiver, "+@"); - - return (yp_node_t *) node; - } - case YP_TOKEN_STRING_BEGIN: { - parser_lex(parser); - - yp_token_t opening = parser->previous; - yp_node_t *node; - - if (accept(parser, YP_TOKEN_STRING_END)) { - // If we get here, then we have an end immediately after a start. In - // that case we'll create an empty content token and return an - // uninterpolated string. - yp_token_t content = (yp_token_t) { - .type = YP_TOKEN_STRING_CONTENT, - .start = parser->previous.start, - .end = parser->previous.start - }; - - node = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_NONE); - } else if (accept(parser, YP_TOKEN_LABEL_END)) { - // If we get here, then we have an end of a label immediately after a - // start. In that case we'll create an empty symbol node. - yp_token_t opening = not_provided(parser); - yp_token_t content = (yp_token_t) { - .type = YP_TOKEN_STRING_CONTENT, - .start = parser->previous.start, - .end = parser->previous.start - }; - - return (yp_node_t *) yp_symbol_node_create(parser, &opening, &content, &parser->previous); - } else if (!lex_mode->as.string.interpolation) { - // If we don't accept interpolation then we expect the string to start - // with a single string content node. - expect(parser, YP_TOKEN_STRING_CONTENT, "Expected string content after opening delimiter."); - yp_token_t content = parser->previous; - - // It is unfortunately possible to have multiple string content nodes in - // a row in the case that there's heredoc content in the middle of the - // string, like this cursed example: - // - // <<-END+'b - // a - // END - // c'+'d' - // - // In that case we need to switch to an interpolated string to be able - // to contain all of the parts. - if (match_type_p(parser, YP_TOKEN_STRING_CONTENT)) { - yp_node_list_t parts; - yp_node_list_init(&parts); - - yp_token_t delimiters = not_provided(parser); - yp_node_t *part = (yp_node_t *) yp_string_node_create_and_unescape(parser, &delimiters, &content, &delimiters, YP_UNESCAPE_MINIMAL); - yp_node_list_append(&parts, part); - - while (accept(parser, YP_TOKEN_STRING_CONTENT)) { - part = (yp_node_t *) yp_string_node_create_and_unescape(parser, &delimiters, &parser->previous, &delimiters, YP_UNESCAPE_MINIMAL); - yp_node_list_append(&parts, part); - } - - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a string literal."); - return (yp_node_t *) yp_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); - } - - if (accept(parser, YP_TOKEN_LABEL_END)) { - return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &content, &parser->previous); - } - - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for a string literal."); - node = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_MINIMAL); - } else if (match_type_p(parser, YP_TOKEN_STRING_CONTENT)) { - // In this case we've hit string content so we know the string at - // least has something in it. We'll need to check if the following - // token is the end (in which case we can return a plain string) or if - // it's not then it has interpolation. - yp_token_t content = parser->current; - parser_lex(parser); - - if (accept(parser, YP_TOKEN_STRING_END)) { - node = (yp_node_t *) yp_string_node_create_and_unescape(parser, &opening, &content, &parser->previous, YP_UNESCAPE_ALL); - } else if (accept(parser, YP_TOKEN_LABEL_END)) { - return (yp_node_t *) yp_symbol_node_create_and_unescape(parser, &opening, &content, &parser->previous); - } else { - // If we get here, then we have interpolation so we'll need to create - // a string or symbol node with interpolation. - yp_node_list_t parts; - yp_node_list_init(&parts); - - yp_token_t string_opening = not_provided(parser); - yp_token_t string_closing = not_provided(parser); - yp_node_t *part = (yp_node_t *) yp_string_node_create_and_unescape(parser, &string_opening, &parser->previous, &string_closing, YP_UNESCAPE_ALL); - yp_node_list_append(&parts, part); - - while (!match_any_type_p(parser, 3, YP_TOKEN_STRING_END, YP_TOKEN_LABEL_END, YP_TOKEN_EOF)) { - yp_node_t *part = parse_string_part(parser); - if (part != NULL) yp_node_list_append(&parts, part); - } - - if (accept(parser, YP_TOKEN_LABEL_END)) { - return (yp_node_t *) yp_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous); - } - - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for an interpolated string."); - node = (yp_node_t *) yp_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); - } - } else { - // If we get here, then the first part of the string is not plain string - // content, in which case we need to parse the string as an interpolated - // string. - yp_node_list_t parts; - yp_node_list_init(&parts); - - while (!match_any_type_p(parser, 3, YP_TOKEN_STRING_END, YP_TOKEN_LABEL_END, YP_TOKEN_EOF)) { - yp_node_t *part = parse_string_part(parser); - if (part != NULL) yp_node_list_append(&parts, part); - } - - if (accept(parser, YP_TOKEN_LABEL_END)) { - return (yp_node_t *) yp_interpolated_symbol_node_create(parser, &opening, &parts, &parser->previous); - } - - expect(parser, YP_TOKEN_STRING_END, "Expected a closing delimiter for an interpolated string."); - node = (yp_node_t *) yp_interpolated_string_node_create(parser, &opening, &parts, &parser->previous); - } - - // If there's a string immediately following this string, then it's a - // concatenatation. In this case we'll parse the next string and create a - // node in the tree that concatenates the two strings. - if (parser->current.type == YP_TOKEN_STRING_BEGIN) { - return (yp_node_t *) yp_string_concat_node_create( - parser, - node, - parse_expression(parser, YP_BINDING_POWER_CALL, "Expected string on the right side of concatenation.") - ); - } else { - return node; - } - } - case YP_TOKEN_SYMBOL_BEGIN: - parser_lex(parser); - return parse_symbol(parser, lex_mode, YP_LEX_STATE_END); - default: - if (context_recoverable(parser, &parser->current)) { - parser->recovering = true; - } - - return (yp_node_t *) yp_missing_node_create(parser, parser->previous.start, parser->previous.end); - } -} - -static inline yp_node_t * -parse_assignment_value(yp_parser_t *parser, yp_binding_power_t previous_binding_power, yp_binding_power_t binding_power, const char *message) { - yp_node_t *value = parse_starred_expression(parser, binding_power, message); - - if (previous_binding_power == YP_BINDING_POWER_STATEMENT && accept(parser, YP_TOKEN_COMMA)) { - yp_token_t opening = not_provided(parser); - yp_token_t closing = not_provided(parser); - yp_array_node_t *array = yp_array_node_create(parser, &opening, &closing); - - yp_array_node_elements_append(array, value); - value = (yp_node_t *) array; - - do { - yp_node_t *element = parse_starred_expression(parser, binding_power, "Expected an element for the array."); - yp_array_node_elements_append(array, element); - if (element->type == YP_NODE_MISSING_NODE) break; - } while (accept(parser, YP_TOKEN_COMMA)); - } - - return value; -} - -static inline yp_node_t * -parse_expression_infix(yp_parser_t *parser, yp_node_t *node, yp_binding_power_t previous_binding_power, yp_binding_power_t binding_power) { - yp_token_t token = parser->current; - - switch (token.type) { - case YP_TOKEN_EQUAL: { - switch (node->type) { - case YP_NODE_CALL_NODE: { - // If we have no arguments to the call node and we need this to be a - // target then this is either a method call or a local variable write. - // This _must_ happen before the value is parsed because it could be - // referenced in the value. - yp_call_node_t *call_node = (yp_call_node_t *) node; - if (yp_call_node_vcall_p(call_node)) { - yp_parser_local_add(parser, &call_node->message); - } - } - /* fallthrough */ - case YP_CASE_WRITABLE: { - parser_lex(parser); - yp_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, "Expected a value after =."); - return parse_target(parser, node, &token, value); - } - case YP_NODE_SPLAT_NODE: { - yp_splat_node_t *splat_node = (yp_splat_node_t *) node; - - switch (splat_node->expression->type) { - case YP_CASE_WRITABLE: { - parser_lex(parser); - yp_node_t *value = parse_assignment_value(parser, previous_binding_power, binding_power, "Expected a value after =."); - return parse_target(parser, (yp_node_t *) splat_node, &token, value); - } - default: {} - } - } - /* fallthrough */ - default: - parser_lex(parser); - - // In this case we have an = sign, but we don't know what it's for. We - // need to treat it as an error. For now, we'll mark it as an error - // and just skip right past it. - yp_diagnostic_list_append(&parser->error_list, token.start, token.end, "Unexpected `='."); - return node; - } - } - case YP_TOKEN_AMPERSAND_AMPERSAND_EQUAL: { - switch (node->type) { - case YP_NODE_CALL_NODE: { - // If we have no arguments to the call node and we need this to be a - // target then this is either a method call or a local variable write. - // This _must_ happen before the value is parsed because it could be - // referenced in the value. - yp_call_node_t *call_node = (yp_call_node_t *) node; - if (yp_call_node_vcall_p(call_node)) { - yp_parser_local_add(parser, &call_node->message); - } - } - /* fallthrough */ - case YP_CASE_WRITABLE: { - yp_token_t operator = parser->current; - parser_lex(parser); - - yp_token_t target_operator = not_provided(parser); - node = parse_target(parser, node, &target_operator, NULL); - - if (node->type == YP_NODE_MULTI_WRITE_NODE) { - yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "Cannot use `&&=' on a multi-write."); - } - - yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after &&="); - return (yp_node_t *) yp_operator_and_assignment_node_create(parser, node, &token, value); - } - default: - parser_lex(parser); - - // In this case we have an &&= sign, but we don't know what it's for. - // We need to treat it as an error. For now, we'll mark it as an error - // and just skip right past it. - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected `&&='."); - return node; - } - } - case YP_TOKEN_PIPE_PIPE_EQUAL: { - switch (node->type) { - case YP_NODE_CALL_NODE: { - // If we have no arguments to the call node and we need this to be a - // target then this is either a method call or a local variable write. - // This _must_ happen before the value is parsed because it could be - // referenced in the value. - yp_call_node_t *call_node = (yp_call_node_t *) node; - if (yp_call_node_vcall_p(call_node)) { - yp_parser_local_add(parser, &call_node->message); - } - } - /* fallthrough */ - case YP_CASE_WRITABLE: { - yp_token_t operator = parser->current; - parser_lex(parser); - - yp_token_t target_operator = not_provided(parser); - node = parse_target(parser, node, &target_operator, NULL); - - if (node->type == YP_NODE_MULTI_WRITE_NODE) { - yp_diagnostic_list_append(&parser->error_list, operator.start, operator.end, "Cannot use `||=' on a multi-write."); - } - - yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after ||="); - return (yp_node_t *) yp_operator_or_assignment_node_create(parser, node, &token, value); - } - default: - parser_lex(parser); - - // In this case we have an ||= sign, but we don't know what it's for. - // We need to treat it as an error. For now, we'll mark it as an error - // and just skip right past it. - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected `||='."); - return node; - } - } - case YP_TOKEN_AMPERSAND_EQUAL: - case YP_TOKEN_CARET_EQUAL: - case YP_TOKEN_GREATER_GREATER_EQUAL: - case YP_TOKEN_LESS_LESS_EQUAL: - case YP_TOKEN_MINUS_EQUAL: - case YP_TOKEN_PERCENT_EQUAL: - case YP_TOKEN_PIPE_EQUAL: - case YP_TOKEN_PLUS_EQUAL: - case YP_TOKEN_SLASH_EQUAL: - case YP_TOKEN_STAR_EQUAL: - case YP_TOKEN_STAR_STAR_EQUAL: { - switch (node->type) { - case YP_NODE_CALL_NODE: { - // If we have no arguments to the call node and we need this to be a - // target then this is either a method call or a local variable write. - // This _must_ happen before the value is parsed because it could be - // referenced in the value. - yp_call_node_t *call_node = (yp_call_node_t *) node; - if (yp_call_node_vcall_p(call_node)) { - yp_parser_local_add(parser, &call_node->message); - } - } - /* fallthrough */ - case YP_CASE_WRITABLE: { - yp_token_t operator = not_provided(parser); - node = parse_target(parser, node, &operator, NULL); - - parser_lex(parser); - yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the operator."); - return (yp_node_t *) yp_operator_assignment_node_create(parser, node, &token, value); - } - default: - parser_lex(parser); - - // In this case we have an operator but we don't know what it's for. - // We need to treat it as an error. For now, we'll mark it as an error - // and just skip right past it. - yp_diagnostic_list_append(&parser->error_list, parser->previous.start, parser->previous.end, "Unexpected operator."); - return node; - } - } - case YP_TOKEN_AMPERSAND_AMPERSAND: - case YP_TOKEN_KEYWORD_AND: { - parser_lex(parser); - - yp_node_t *right = parse_expression(parser, binding_power, "Expected a value after the operator."); - return (yp_node_t *) yp_and_node_create(parser, node, &token, right); - } - case YP_TOKEN_KEYWORD_OR: - case YP_TOKEN_PIPE_PIPE: { - parser_lex(parser); - - yp_node_t *right = parse_expression(parser, binding_power, "Expected a value after the operator."); - return (yp_node_t *) yp_or_node_create(parser, node, &token, right); - } - case YP_TOKEN_EQUAL_TILDE: { - // Note that we _must_ parse the value before adding the local variables - // in order to properly mirror the behavior of Ruby. For example, - // - // /(?bar)/ =~ foo - // - // In this case, `foo` should be a method call and not a local yet. - parser_lex(parser); - yp_node_t *argument = parse_expression(parser, binding_power, "Expected a value after the operator."); - - // If the receiver of this =~ is a regular expression node, then we need - // to introduce local variables for it based on its named capture groups. - if (node->type == YP_NODE_REGULAR_EXPRESSION_NODE) { - yp_string_list_t named_captures; - yp_string_list_init(&named_captures); - - yp_token_t *content = &((yp_regular_expression_node_t *) node)->content; - - __attribute__((unused)) bool captured_group_names = - yp_regexp_named_capture_group_names(content->start, (size_t) (content->end - content->start), &named_captures); - // We assert that the the regex was successfully parsed - assert(captured_group_names); - - for (size_t index = 0; index < named_captures.length; index++) { - yp_string_t *name = &named_captures.strings[index]; - assert(name->type == YP_STRING_SHARED); - - yp_parser_local_add(parser, &(yp_token_t) { - .type = YP_TOKEN_IDENTIFIER, - .start = name->as.shared.start, - .end = name->as.shared.end - }); - } - - yp_string_list_free(&named_captures); - } - - return (yp_node_t *) yp_call_node_binary_create(parser, node, &token, argument); - } - case YP_TOKEN_BANG_EQUAL: - case YP_TOKEN_BANG_TILDE: - case YP_TOKEN_EQUAL_EQUAL: - case YP_TOKEN_EQUAL_EQUAL_EQUAL: - case YP_TOKEN_LESS_EQUAL_GREATER: - case YP_TOKEN_GREATER: - case YP_TOKEN_GREATER_EQUAL: - case YP_TOKEN_LESS: - case YP_TOKEN_LESS_EQUAL: - case YP_TOKEN_CARET: - case YP_TOKEN_PIPE: - case YP_TOKEN_AMPERSAND: - case YP_TOKEN_GREATER_GREATER: - case YP_TOKEN_LESS_LESS: - case YP_TOKEN_MINUS: - case YP_TOKEN_PLUS: - case YP_TOKEN_PERCENT: - case YP_TOKEN_SLASH: - case YP_TOKEN_STAR: - case YP_TOKEN_STAR_STAR: { - parser_lex(parser); - - yp_node_t *argument = parse_expression(parser, binding_power, "Expected a value after the operator."); - return (yp_node_t *) yp_call_node_binary_create(parser, node, &token, argument); - } - case YP_TOKEN_AMPERSAND_DOT: - case YP_TOKEN_DOT: { - parser_lex(parser); - yp_token_t operator = parser->previous; - yp_arguments_t arguments = yp_arguments(parser); - - // This if statement handles the foo.() syntax. - if (match_type_p(parser, YP_TOKEN_PARENTHESIS_LEFT)) { - parse_arguments_list(parser, &arguments, true); - return (yp_node_t *) yp_call_node_shorthand_create(parser, node, &operator, &arguments); - } - - yp_token_t message; - - switch (parser->current.type) { - case YP_CASE_OPERATOR: - case YP_CASE_KEYWORD: - case YP_TOKEN_CONSTANT: - case YP_TOKEN_IDENTIFIER: { - parser_lex(parser); - message = parser->previous; - break; - } - default: { - yp_diagnostic_list_append(&parser->error_list, parser->current.start, parser->current.end, "Expected a valid method name"); - message = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; - } - } - - parse_arguments_list(parser, &arguments, true); - yp_call_node_t *call = yp_call_node_call_create(parser, node, &operator, &message, &arguments); - - if ( - (previous_binding_power == YP_BINDING_POWER_STATEMENT) && - arguments.arguments == NULL && - arguments.opening.type == YP_TOKEN_NOT_PROVIDED && - match_type_p(parser, YP_TOKEN_COMMA) - ) { - return parse_targets(parser, (yp_node_t *) call, YP_BINDING_POWER_INDEX); - } else { - return (yp_node_t *) call; - } - } - case YP_TOKEN_DOT_DOT: - case YP_TOKEN_DOT_DOT_DOT: { - parser_lex(parser); - - yp_node_t *right = NULL; - if (token_begins_expression_p(parser->current.type)) { - right = parse_expression(parser, binding_power, "Expected a value after the operator."); - } - - return (yp_node_t *) yp_range_node_create(parser, node, &token, right); - } - case YP_TOKEN_KEYWORD_IF_MODIFIER: { - yp_token_t keyword = parser->current; - parser_lex(parser); - - yp_node_t *predicate = parse_expression(parser, binding_power, "Expected a predicate after `if'."); - return (yp_node_t *) yp_if_node_modifier_create(parser, node, &keyword, predicate); - } - case YP_TOKEN_KEYWORD_UNLESS_MODIFIER: { - yp_token_t keyword = parser->current; - parser_lex(parser); - - yp_node_t *predicate = parse_expression(parser, binding_power, "Expected a predicate after `unless'."); - return (yp_node_t *) yp_unless_node_modifier_create(parser, node, &keyword, predicate); - } - case YP_TOKEN_KEYWORD_UNTIL_MODIFIER: { - parser_lex(parser); - yp_statements_node_t *statements = yp_statements_node_create(parser); - yp_statements_node_body_append(statements, node); - - yp_node_t *predicate = parse_expression(parser, binding_power, "Expected a predicate after 'until'"); - return (yp_node_t *) yp_until_node_create(parser, &token, predicate, statements); - } - case YP_TOKEN_KEYWORD_WHILE_MODIFIER: { - parser_lex(parser); - yp_statements_node_t *statements = yp_statements_node_create(parser); - yp_statements_node_body_append(statements, node); - - yp_node_t *predicate = parse_expression(parser, binding_power, "Expected a predicate after 'while'"); - return (yp_node_t *) yp_while_node_create(parser, &token, predicate, statements); - } - case YP_TOKEN_QUESTION_MARK: { - parser_lex(parser); - yp_node_t *true_expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after '?'"); - - if (parser->recovering) { - // If parsing the true expression of this ternary resulted in a syntax - // error that we can recover from, then we're going to put missing nodes - // and tokens into the remaining places. We want to be sure to do this - // before the `expect` function call to make sure it doesn't - // accidentally move past a ':' token that occurs after the syntax - // error. - yp_token_t colon = (yp_token_t) { .type = YP_TOKEN_MISSING, .start = parser->previous.end, .end = parser->previous.end }; - yp_node_t *false_expression = (yp_node_t *) yp_missing_node_create(parser, colon.start, colon.end); - - return (yp_node_t *) yp_if_node_ternary_create(parser, node, &token, true_expression, &colon, false_expression); - } - - accept(parser, YP_TOKEN_NEWLINE); - expect(parser, YP_TOKEN_COLON, "Expected ':' after true expression in ternary operator."); - - yp_token_t colon = parser->previous; - yp_node_t *false_expression = parse_expression(parser, YP_BINDING_POWER_DEFINED, "Expected a value after ':'"); - - return (yp_node_t *) yp_if_node_ternary_create(parser, node, &token, true_expression, &colon, false_expression); - } - case YP_TOKEN_COLON_COLON: { - parser_lex(parser); - yp_token_t delimiter = parser->previous; - - switch (parser->current.type) { - case YP_TOKEN_CONSTANT: { - parser_lex(parser); - yp_node_t *path; - - if ( - (parser->current.type == YP_TOKEN_PARENTHESIS_LEFT) || - (token_begins_expression_p(parser->current.type) || match_any_type_p(parser, 2, YP_TOKEN_USTAR, YP_TOKEN_USTAR_STAR)) - ) { - // If we have a constant immediately following a '::' operator, then - // this can either be a constant path or a method call, depending on - // what follows the constant. - // - // If we have parentheses, then this is a method call. That would - // look like Foo::Bar(). - yp_token_t message = parser->previous; - yp_arguments_t arguments = yp_arguments(parser); - - parse_arguments_list(parser, &arguments, true); - path = (yp_node_t *) yp_call_node_call_create(parser, node, &delimiter, &message, &arguments); - } else { - // Otherwise, this is a constant path. That would look like Foo::Bar. - yp_node_t *child = (yp_node_t *) yp_constant_read_node_create(parser, &parser->previous); - path = (yp_node_t *)yp_constant_path_node_create(parser, node, &delimiter, child); - } - - // If this is followed by a comma then it is a multiple assignment. - if (previous_binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { - return parse_targets(parser, path, YP_BINDING_POWER_INDEX); - } - - return path; - } - case YP_TOKEN_AMPERSAND: - case YP_TOKEN_BACKTICK: - case YP_TOKEN_BANG: - case YP_TOKEN_BANG_EQUAL: - case YP_TOKEN_BANG_TILDE: - case YP_TOKEN_CARET: - case YP_TOKEN_EQUAL_EQUAL: - case YP_TOKEN_EQUAL_EQUAL_EQUAL: - case YP_TOKEN_EQUAL_TILDE: - case YP_TOKEN_GREATER: - case YP_TOKEN_GREATER_EQUAL: - case YP_TOKEN_GREATER_GREATER: - case YP_TOKEN_HEREDOC_START: - case YP_TOKEN_IGNORED_NEWLINE: - case YP_TOKEN_KEYWORD_ALIAS: - case YP_TOKEN_KEYWORD_AND: - case YP_TOKEN_KEYWORD_BEGIN: - case YP_TOKEN_KEYWORD_BEGIN_UPCASE: - case YP_TOKEN_KEYWORD_BREAK: - case YP_TOKEN_KEYWORD_CASE: - case YP_TOKEN_KEYWORD_CLASS: - case YP_TOKEN_KEYWORD_DEF: - case YP_TOKEN_KEYWORD_DEFINED: - case YP_TOKEN_KEYWORD_DO: - case YP_TOKEN_KEYWORD_ELSE: - case YP_TOKEN_KEYWORD_ELSIF: - case YP_TOKEN_KEYWORD_END: - case YP_TOKEN_KEYWORD_END_UPCASE: - case YP_TOKEN_KEYWORD_ENSURE: - case YP_TOKEN_KEYWORD_FALSE: - case YP_TOKEN_KEYWORD_FOR: - case YP_TOKEN_KEYWORD_IF: - case YP_TOKEN_KEYWORD_IN: - case YP_TOKEN_KEYWORD_NEXT: - case YP_TOKEN_KEYWORD_NIL: - case YP_TOKEN_KEYWORD_NOT: - case YP_TOKEN_KEYWORD_OR: - case YP_TOKEN_KEYWORD_REDO: - case YP_TOKEN_KEYWORD_RESCUE: - case YP_TOKEN_KEYWORD_RETRY: - case YP_TOKEN_KEYWORD_RETURN: - case YP_TOKEN_KEYWORD_SELF: - case YP_TOKEN_KEYWORD_SUPER: - case YP_TOKEN_KEYWORD_THEN: - case YP_TOKEN_KEYWORD_TRUE: - case YP_TOKEN_KEYWORD_UNDEF: - case YP_TOKEN_KEYWORD_UNLESS: - case YP_TOKEN_KEYWORD_UNTIL: - case YP_TOKEN_KEYWORD_WHEN: - case YP_TOKEN_KEYWORD_WHILE: - case YP_TOKEN_KEYWORD_YIELD: - case YP_TOKEN_KEYWORD___ENCODING__: - case YP_TOKEN_KEYWORD___FILE__: - case YP_TOKEN_KEYWORD___LINE__: - case YP_TOKEN_LESS: - case YP_TOKEN_LESS_EQUAL: - case YP_TOKEN_LESS_EQUAL_GREATER: - case YP_TOKEN_LESS_LESS: - case YP_TOKEN_MINUS: - case YP_TOKEN_PERCENT: - case YP_TOKEN_PERCENT_LOWER_I: - case YP_TOKEN_PERCENT_LOWER_W: - case YP_TOKEN_PERCENT_LOWER_X: - case YP_TOKEN_PERCENT_UPPER_I: - case YP_TOKEN_PERCENT_UPPER_W: - case YP_TOKEN_PIPE: - case YP_TOKEN_PLUS: - case YP_TOKEN_REGEXP_BEGIN: - case YP_TOKEN_SLASH: - case YP_TOKEN_STAR: - case YP_TOKEN_STAR_STAR: - case YP_TOKEN_TILDE: - case YP_TOKEN_UCOLON_COLON: - case YP_TOKEN_UDOT_DOT: - case YP_TOKEN_UDOT_DOT_DOT: - case YP_TOKEN___END__: - case YP_TOKEN_IDENTIFIER: { - parser_lex(parser); - - // If we have an identifier following a '::' operator, then it is for - // sure a method call. - yp_arguments_t arguments = yp_arguments(parser); - parse_arguments_list(parser, &arguments, true); - yp_call_node_t *call = yp_call_node_call_create(parser, node, &delimiter, &parser->previous, &arguments); - - // If this is followed by a comma then it is a multiple assignment. - if (previous_binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { - return parse_targets(parser, (yp_node_t *) call, YP_BINDING_POWER_INDEX); - } - - return (yp_node_t *) call; - } - case YP_TOKEN_PARENTHESIS_LEFT: { - // If we have a parenthesis following a '::' operator, then it is the - // method call shorthand. That would look like Foo::(bar). - yp_arguments_t arguments = yp_arguments(parser); - parse_arguments_list(parser, &arguments, true); - - return (yp_node_t *) yp_call_node_shorthand_create(parser, node, &delimiter, &arguments); - } - default: { - yp_diagnostic_list_append(&parser->error_list, delimiter.start, delimiter.end, "Expected identifier or constant after '::'"); - yp_node_t *child = (yp_node_t *) yp_missing_node_create(parser, delimiter.start, delimiter.end); - return (yp_node_t *)yp_constant_path_node_create(parser, node, &delimiter, child); - } - } - } - case YP_TOKEN_KEYWORD_RESCUE_MODIFIER: { - parser_lex(parser); - accept(parser, YP_TOKEN_NEWLINE); - yp_node_t *value = parse_expression(parser, binding_power, "Expected a value after the rescue keyword."); - - return (yp_node_t *) yp_rescue_modifier_node_create(parser, node, &token, value); - } - case YP_TOKEN_BRACKET_LEFT: { - parser_lex(parser); - - yp_arguments_t arguments = yp_arguments(parser); - arguments.opening = parser->previous; - - if (!accept(parser, YP_TOKEN_BRACKET_RIGHT)) { - yp_accepts_block_stack_push(parser, true); - arguments.arguments = yp_arguments_node_create(parser); - - parse_arguments(parser, arguments.arguments, false, YP_TOKEN_BRACKET_RIGHT); - yp_accepts_block_stack_pop(parser); - - expect(parser, YP_TOKEN_BRACKET_RIGHT, "Expected ']' to close the bracket expression."); - } - - arguments.closing = parser->previous; - - // If we have a comma after the closing bracket then this is a multiple - // assignment and we should parse the targets. - if (previous_binding_power == YP_BINDING_POWER_STATEMENT && match_type_p(parser, YP_TOKEN_COMMA)) { - yp_call_node_t *aref = yp_call_node_aref_create(parser, node, &arguments); - return parse_targets(parser, (yp_node_t *) aref, YP_BINDING_POWER_INDEX); - } - - // If we're at the end of the arguments, we can now check if there is a - // block node that starts with a {. If there is, then we can parse it and - // add it to the arguments. - if (accept(parser, YP_TOKEN_BRACE_LEFT)) { - arguments.block = parse_block(parser); - } else if (yp_accepts_block_stack_p(parser) && accept(parser, YP_TOKEN_KEYWORD_DO)) { - arguments.block = parse_block(parser); - } - - return (yp_node_t *) yp_call_node_aref_create(parser, node, &arguments); - } - case YP_TOKEN_KEYWORD_IN: { - bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; - parser->pattern_matching_newlines = true; - - yp_token_t operator = parser->current; - parser->command_start = false; - lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); - - parser_lex(parser); - - yp_node_t *pattern = parse_pattern(parser, true, "Expected a pattern after `in'."); - parser->pattern_matching_newlines = previous_pattern_matching_newlines; - - return (yp_node_t *) yp_match_predicate_node_create(parser, node, pattern, &operator); - } - case YP_TOKEN_EQUAL_GREATER: { - bool previous_pattern_matching_newlines = parser->pattern_matching_newlines; - parser->pattern_matching_newlines = true; - - yp_token_t operator = parser->current; - parser->command_start = false; - lex_state_set(parser, YP_LEX_STATE_BEG | YP_LEX_STATE_LABEL); - - parser_lex(parser); - - yp_node_t *pattern = parse_pattern(parser, true, "Expected a pattern after `=>'."); - parser->pattern_matching_newlines = previous_pattern_matching_newlines; - - return (yp_node_t *) yp_match_required_node_create(parser, node, pattern, &operator); - } - default: - assert(false && "unreachable"); - return NULL; - } -} - -// Parse an expression at the given point of the parser using the given binding -// power to parse subsequent chains. If this function finds a syntax error, it -// will append the error message to the parser's error list. -// -// Consumers of this function should always check parser->recovering to -// determine if they need to perform additional cleanup. -static yp_node_t * -parse_expression(yp_parser_t *parser, yp_binding_power_t binding_power, const char *message) { - yp_token_t recovery = parser->previous; - yp_node_t *node = parse_expression_prefix(parser, binding_power); - - // If we found a syntax error, then the type of node returned by - // parse_expression_prefix is going to be a missing node. In that case we need - // to add the error message to the parser's error list. - if (node->type == YP_NODE_MISSING_NODE) { - yp_diagnostic_list_append(&parser->error_list, recovery.end, recovery.end, message); - return node; - } - - // Otherwise we'll look and see if the next token can be parsed as an infix - // operator. If it can, then we'll parse it using parse_expression_infix. - yp_binding_powers_t current_binding_powers; - while ( - current_binding_powers = yp_binding_powers[parser->current.type], - binding_power <= current_binding_powers.left && - current_binding_powers.binary - ) { - node = parse_expression_infix(parser, node, binding_power, current_binding_powers.right); - } - - return node; -} - -static yp_node_t * -parse_program(yp_parser_t *parser) { - yp_parser_scope_push(parser, true); - parser_lex(parser); - - yp_statements_node_t *statements = parse_statements(parser, YP_CONTEXT_MAIN); - yp_token_list_t locals = parser->current_scope->locals; - yp_parser_scope_pop(parser); - - // If this is an empty file, then we're still going to parse all of the - // statements in order to gather up all of the comments and such. Here we'll - // correct the location information. - if (yp_statements_node_body_length(statements) == 0) { - yp_statements_node_location_set(statements, parser->start, parser->start); - } - - return (yp_node_t *) yp_program_node_create(parser, &locals, statements); -} - -/******************************************************************************/ -/* External functions */ -/******************************************************************************/ - -// Initialize a parser with the given start and end pointers. -__attribute__((__visibility__("default"))) extern void -yp_parser_init(yp_parser_t *parser, const char *source, size_t size, const char *filepath) { - // Set filepath to the file that was passed - if (!filepath) filepath = ""; - yp_string_t filepath_string; - yp_string_constant_init(&filepath_string, filepath, strlen(filepath)); - - *parser = (yp_parser_t) { - .lex_state = YP_LEX_STATE_BEG, - .command_start = true, - .enclosure_nesting = 0, - .lambda_enclosure_nesting = -1, - .brace_nesting = 0, - .lex_modes = { - .index = 0, - .stack = {{ .mode = YP_LEX_DEFAULT }}, - .current = &parser->lex_modes.stack[0], - }, - .start = source, - .end = source + size, - .previous = { .type = YP_TOKEN_EOF, .start = source, .end = source }, - .current = { .type = YP_TOKEN_EOF, .start = source, .end = source }, - .next_start = NULL, - .heredoc_end = NULL, - .current_scope = NULL, - .current_context = NULL, - .recovering = false, - .encoding = yp_encoding_utf_8, - .encoding_changed_callback = NULL, - .encoding_decode_callback = NULL, - .encoding_comment_start = source, - .lex_callback = NULL, - .pattern_matching_newlines = false, - .in_keyword_arg = false, - .filepath_string = filepath_string, - }; - - yp_state_stack_init(&parser->do_loop_stack); - yp_state_stack_init(&parser->accepts_block_stack); - yp_accepts_block_stack_push(parser, true); - - yp_list_init(&parser->warning_list); - yp_list_init(&parser->error_list); - yp_list_init(&parser->comment_list); - - if (size >= 3 && (unsigned char) source[0] == 0xef && (unsigned char) source[1] == 0xbb && (unsigned char) source[2] == 0xbf) { - // If the first three bytes of the source are the UTF-8 BOM, then we'll skip - // over them. - parser->current.end += 3; - } else if (size >= 2 && source[0] == '#' && source[1] == '!') { - // If the first two bytes of the source are a shebang, then we'll indicate - // that the encoding comment is at the end of the shebang. - const char *encoding_comment_start = memchr(source, '\n', size); - if (encoding_comment_start) { - parser->encoding_comment_start = encoding_comment_start + 1; - } - } -} - -// Register a callback that will be called whenever YARP changes the encoding it -// is using to parse based on the magic comment. -__attribute__((__visibility__("default"))) extern void -yp_parser_register_encoding_changed_callback(yp_parser_t *parser, yp_encoding_changed_callback_t callback) { - parser->encoding_changed_callback = callback; -} - -// Register a callback that will be called when YARP encounters a magic comment -// with an encoding referenced that it doesn't understand. The callback should -// return NULL if it also doesn't understand the encoding or it should return a -// pointer to a yp_encoding_t struct that contains the functions necessary to -// parse identifiers. -__attribute__((__visibility__("default"))) extern void -yp_parser_register_encoding_decode_callback(yp_parser_t *parser, yp_encoding_decode_callback_t callback) { - parser->encoding_decode_callback = callback; -} - -// Free all of the memory associated with the comment list. -static inline void -yp_comment_list_free(yp_list_t *list) { - yp_list_node_t *node, *next; - - for (node = list->head; node != NULL; node = next) { - next = node->next; - - yp_comment_t *comment = (yp_comment_t *) node; - free(comment); - } -} - -// Free any memory associated with the given parser. -__attribute__((__visibility__("default"))) extern void -yp_parser_free(yp_parser_t *parser) { - yp_string_free(&parser->filepath_string); - yp_diagnostic_list_free(&parser->error_list); - yp_diagnostic_list_free(&parser->warning_list); - yp_comment_list_free(&parser->comment_list); -} - -// Parse the Ruby source associated with the given parser and return the tree. -__attribute__((__visibility__("default"))) extern yp_node_t * -yp_parse(yp_parser_t *parser) { - return parse_program(parser); -} - -__attribute__((__visibility__("default"))) extern void -yp_serialize(yp_parser_t *parser, yp_node_t *node, yp_buffer_t *buffer) { - yp_buffer_append_str(buffer, "YARP", 4); - yp_buffer_append_u8(buffer, YP_VERSION_MAJOR); - yp_buffer_append_u8(buffer, YP_VERSION_MINOR); - yp_buffer_append_u8(buffer, YP_VERSION_PATCH); - - yp_serialize_node(parser, node, buffer); - yp_buffer_append_str(buffer, "\0", 1); -} - -// Parse and serialize the AST represented by the given source to the given -// buffer. -__attribute__((__visibility__("default"))) extern void -yp_parse_serialize(const char *source, size_t size, yp_buffer_t *buffer) { - yp_parser_t parser; - yp_parser_init(&parser, source, size, NULL); - - yp_node_t *node = yp_parse(&parser); - yp_serialize(&parser, node, buffer); - - yp_node_destroy(&parser, node); - yp_parser_free(&parser); -} - -#undef YP_CASE_KEYWORD -#undef YP_CASE_OPERATOR -#undef YP_CASE_WRITABLE -#undef YP_STRINGIZE -#undef YP_STRINGIZE0 -#undef YP_VERSION_MACRO diff --git a/src/yarp/java/org/yarp/AbstractNodeVisitor.java b/src/yarp/java/org/yarp/AbstractNodeVisitor.java index ee50d7bd1247..b90a1cded0eb 100644 --- a/src/yarp/java/org/yarp/AbstractNodeVisitor.java +++ b/src/yarp/java/org/yarp/AbstractNodeVisitor.java @@ -1,7 +1,7 @@ /******************************************************************************/ /* This file is generated by the bin/template script and should not be */ /* modified manually. See */ -/* bin/templates/java/org/yarp/AbstractNodeVisitor.java.erb */ +/* templates/java/org/yarp/AbstractNodeVisitor.java.erb */ /* if you are looking to modify the */ /* template */ /******************************************************************************/ @@ -44,6 +44,10 @@ public T visitAssocSplatNode(Nodes.AssocSplatNode node) { return defaultVisit(node); } + public T visitBackReferenceReadNode(Nodes.BackReferenceReadNode node) { + return defaultVisit(node); + } + public T visitBeginNode(Nodes.BeginNode node) { return defaultVisit(node); } @@ -72,6 +76,18 @@ public T visitCallNode(Nodes.CallNode node) { return defaultVisit(node); } + public T visitCallOperatorAndWriteNode(Nodes.CallOperatorAndWriteNode node) { + return defaultVisit(node); + } + + public T visitCallOperatorOrWriteNode(Nodes.CallOperatorOrWriteNode node) { + return defaultVisit(node); + } + + public T visitCallOperatorWriteNode(Nodes.CallOperatorWriteNode node) { + return defaultVisit(node); + } + public T visitCapturePatternNode(Nodes.CapturePatternNode node) { return defaultVisit(node); } @@ -84,6 +100,18 @@ public T visitClassNode(Nodes.ClassNode node) { return defaultVisit(node); } + public T visitClassVariableOperatorAndWriteNode(Nodes.ClassVariableOperatorAndWriteNode node) { + return defaultVisit(node); + } + + public T visitClassVariableOperatorOrWriteNode(Nodes.ClassVariableOperatorOrWriteNode node) { + return defaultVisit(node); + } + + public T visitClassVariableOperatorWriteNode(Nodes.ClassVariableOperatorWriteNode node) { + return defaultVisit(node); + } + public T visitClassVariableReadNode(Nodes.ClassVariableReadNode node) { return defaultVisit(node); } @@ -92,10 +120,34 @@ public T visitClassVariableWriteNode(Nodes.ClassVariableWriteNode node) { return defaultVisit(node); } + public T visitConstantOperatorAndWriteNode(Nodes.ConstantOperatorAndWriteNode node) { + return defaultVisit(node); + } + + public T visitConstantOperatorOrWriteNode(Nodes.ConstantOperatorOrWriteNode node) { + return defaultVisit(node); + } + + public T visitConstantOperatorWriteNode(Nodes.ConstantOperatorWriteNode node) { + return defaultVisit(node); + } + public T visitConstantPathNode(Nodes.ConstantPathNode node) { return defaultVisit(node); } + public T visitConstantPathOperatorAndWriteNode(Nodes.ConstantPathOperatorAndWriteNode node) { + return defaultVisit(node); + } + + public T visitConstantPathOperatorOrWriteNode(Nodes.ConstantPathOperatorOrWriteNode node) { + return defaultVisit(node); + } + + public T visitConstantPathOperatorWriteNode(Nodes.ConstantPathOperatorWriteNode node) { + return defaultVisit(node); + } + public T visitConstantPathWriteNode(Nodes.ConstantPathWriteNode node) { return defaultVisit(node); } @@ -116,6 +168,14 @@ public T visitElseNode(Nodes.ElseNode node) { return defaultVisit(node); } + public T visitEmbeddedStatementsNode(Nodes.EmbeddedStatementsNode node) { + return defaultVisit(node); + } + + public T visitEmbeddedVariableNode(Nodes.EmbeddedVariableNode node) { + return defaultVisit(node); + } + public T visitEnsureNode(Nodes.EnsureNode node) { return defaultVisit(node); } @@ -148,6 +208,18 @@ public T visitForwardingSuperNode(Nodes.ForwardingSuperNode node) { return defaultVisit(node); } + public T visitGlobalVariableOperatorAndWriteNode(Nodes.GlobalVariableOperatorAndWriteNode node) { + return defaultVisit(node); + } + + public T visitGlobalVariableOperatorOrWriteNode(Nodes.GlobalVariableOperatorOrWriteNode node) { + return defaultVisit(node); + } + + public T visitGlobalVariableOperatorWriteNode(Nodes.GlobalVariableOperatorWriteNode node) { + return defaultVisit(node); + } + public T visitGlobalVariableReadNode(Nodes.GlobalVariableReadNode node) { return defaultVisit(node); } @@ -176,6 +248,18 @@ public T visitInNode(Nodes.InNode node) { return defaultVisit(node); } + public T visitInstanceVariableOperatorAndWriteNode(Nodes.InstanceVariableOperatorAndWriteNode node) { + return defaultVisit(node); + } + + public T visitInstanceVariableOperatorOrWriteNode(Nodes.InstanceVariableOperatorOrWriteNode node) { + return defaultVisit(node); + } + + public T visitInstanceVariableOperatorWriteNode(Nodes.InstanceVariableOperatorWriteNode node) { + return defaultVisit(node); + } + public T visitInstanceVariableReadNode(Nodes.InstanceVariableReadNode node) { return defaultVisit(node); } @@ -220,6 +304,18 @@ public T visitLambdaNode(Nodes.LambdaNode node) { return defaultVisit(node); } + public T visitLocalVariableOperatorAndWriteNode(Nodes.LocalVariableOperatorAndWriteNode node) { + return defaultVisit(node); + } + + public T visitLocalVariableOperatorOrWriteNode(Nodes.LocalVariableOperatorOrWriteNode node) { + return defaultVisit(node); + } + + public T visitLocalVariableOperatorWriteNode(Nodes.LocalVariableOperatorWriteNode node) { + return defaultVisit(node); + } + public T visitLocalVariableReadNode(Nodes.LocalVariableReadNode node) { return defaultVisit(node); } @@ -260,15 +356,7 @@ public T visitNoKeywordsParameterNode(Nodes.NoKeywordsParameterNode node) { return defaultVisit(node); } - public T visitOperatorAndAssignmentNode(Nodes.OperatorAndAssignmentNode node) { - return defaultVisit(node); - } - - public T visitOperatorAssignmentNode(Nodes.OperatorAssignmentNode node) { - return defaultVisit(node); - } - - public T visitOperatorOrAssignmentNode(Nodes.OperatorOrAssignmentNode node) { + public T visitNumberedReferenceReadNode(Nodes.NumberedReferenceReadNode node) { return defaultVisit(node); } @@ -384,10 +472,6 @@ public T visitStringConcatNode(Nodes.StringConcatNode node) { return defaultVisit(node); } - public T visitStringInterpolatedNode(Nodes.StringInterpolatedNode node) { - return defaultVisit(node); - } - public T visitStringNode(Nodes.StringNode node) { return defaultVisit(node); } diff --git a/src/yarp/java/org/yarp/Loader.java b/src/yarp/java/org/yarp/Loader.java index 0f33aef982c2..1b0d5cfc03a1 100644 --- a/src/yarp/java/org/yarp/Loader.java +++ b/src/yarp/java/org/yarp/Loader.java @@ -1,7 +1,7 @@ /******************************************************************************/ /* This file is generated by the bin/template script and should not be */ /* modified manually. See */ -/* bin/templates/java/org/yarp/Loader.java.erb */ +/* templates/java/org/yarp/Loader.java.erb */ /* if you are looking to modify the */ /* template */ /******************************************************************************/ @@ -15,16 +15,46 @@ public class Loader { public static Nodes.Node load(byte[] source, byte[] serialized) { - return new Loader(source, serialized).load(); + return new Loader(serialized).load(source); + } + + private static final class ConstantPool { + + private final byte[] source; + private final int bufferOffset; + private final byte[][] cache; + + ConstantPool(byte[] source, int bufferOffset, int length) { + this.source = source; + this.bufferOffset = bufferOffset; + cache = new byte[length][]; + } + + byte[] get(ByteBuffer buffer, int oneBasedIndex) { + int index = oneBasedIndex - 1; + byte[] constant = cache[index]; + if (constant == null) { + int offset = bufferOffset + index * 8; + int start = buffer.getInt(offset); + int length = buffer.getInt(offset + 4); + + constant = new byte[length]; + System.arraycopy(source, start, constant, 0, length); + cache[index] = constant; + } + return constant; + } + } private final ByteBuffer buffer; + private ConstantPool constantPool; - private Loader(byte[] source, byte[] serialized) { + private Loader(byte[] serialized) { buffer = ByteBuffer.wrap(serialized).order(ByteOrder.nativeOrder()); } - private Nodes.Node load() { + private Nodes.Node load(byte[] source) { expect((byte) 'Y'); expect((byte) 'A'); expect((byte) 'R'); @@ -34,11 +64,19 @@ private Nodes.Node load() { expect((byte) 4); expect((byte) 0); - Nodes.Node node = loadNode(); + // This loads the name of the encoding. We don't actually do anything + // with it just yet. + int encodingLength = loadVarInt(); + byte[] encodingName = new byte[encodingLength]; + buffer.get(encodingName); - expect((byte) 0); // yp_serialize() appends a final \0 byte + int constantPoolBufferOffset = buffer.getInt(); + int constantPoolLength = loadVarInt(); + this.constantPool = new ConstantPool(source, constantPoolBufferOffset, constantPoolLength); + + Nodes.Node node = loadNode(); + int left = constantPoolBufferOffset - buffer.position(); - int left = buffer.capacity() - buffer.position(); if (left != 0) { throw new Error("Expected to consume all bytes while deserializing but there were " + left + " bytes left"); } @@ -53,31 +91,35 @@ private byte[] loadString() { return string; } - private Nodes.Token loadOptionalToken() { + private Nodes.Node loadOptionalNode() { if (buffer.get(buffer.position()) != 0) { - return loadToken(); + return loadNode(); } else { buffer.position(buffer.position() + 1); // continue after the 0 byte return null; } } - private Nodes.Node loadOptionalNode() { - if (buffer.get(buffer.position()) != 0) { - return loadNode(); - } else { - buffer.position(buffer.position() + 1); // continue after the 0 byte - return null; + private Nodes.Location[] loadLocations() { + int length = loadVarInt(); + Nodes.Location[] locations = new Nodes.Location[length]; + for (int i = 0; i < length; i++) { + locations[i] = loadLocation(); } + return locations; + } + + private byte[] loadConstant() { + return constantPool.get(buffer, loadVarInt()); } - private Nodes.Token[] loadTokens() { + private byte[][] loadConstants() { int length = loadVarInt(); - Nodes.Token[] tokens = new Nodes.Token[length]; + byte[][] constants = new byte[length][]; for (int i = 0; i < length; i++) { - tokens[i] = loadToken(); + constants[i] = constantPool.get(buffer, loadVarInt()); } - return tokens; + return constants; } private Nodes.Node[] loadNodes() { @@ -89,21 +131,8 @@ private Nodes.Node[] loadNodes() { return nodes; } - private Nodes.Token loadToken() { - int type = buffer.get() & 0xFF; - int startOffset = loadVarInt(); - int length = loadVarInt(); - int endOffset = startOffset + length; - - final Nodes.TokenType tokenType = Nodes.TOKEN_TYPES[type]; - return new Nodes.Token(tokenType, startOffset, endOffset); - } - private Nodes.Location loadLocation() { - int startOffset = loadVarInt(); - int length = loadVarInt(); - int endOffset = startOffset + length; - return new Nodes.Location(startOffset, endOffset); + return new Nodes.Location(loadVarInt(), loadVarInt()); } private Nodes.Location loadOptionalLocation() { @@ -136,219 +165,260 @@ private Nodes.Node loadNode() { int type = buffer.get() & 0xFF; int startOffset = loadVarInt(); int length = loadVarInt(); - int endOffset = startOffset + length; switch (type) { case 1: - return new Nodes.AliasNode(loadNode(), loadNode(), loadLocation(), startOffset, endOffset); + return new Nodes.AliasNode(loadNode(), loadNode(), loadLocation(), startOffset, length); case 2: - return new Nodes.AlternationPatternNode(loadNode(), loadNode(), loadLocation(), startOffset, endOffset); + return new Nodes.AlternationPatternNode(loadNode(), loadNode(), loadLocation(), startOffset, length); case 3: - return new Nodes.AndNode(loadNode(), loadNode(), loadToken(), startOffset, endOffset); + return new Nodes.AndNode(loadNode(), loadNode(), loadLocation(), startOffset, length); case 4: - return new Nodes.ArgumentsNode(loadNodes(), startOffset, endOffset); + return new Nodes.ArgumentsNode(loadNodes(), startOffset, length); case 5: - return new Nodes.ArrayNode(loadNodes(), loadOptionalToken(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.ArrayNode(loadNodes(), loadOptionalLocation(), loadOptionalLocation(), startOffset, length); case 6: - return new Nodes.ArrayPatternNode(loadOptionalNode(), loadNodes(), loadOptionalNode(), loadNodes(), loadOptionalLocation(), loadOptionalLocation(), startOffset, endOffset); + return new Nodes.ArrayPatternNode(loadOptionalNode(), loadNodes(), loadOptionalNode(), loadNodes(), loadOptionalLocation(), loadOptionalLocation(), startOffset, length); case 7: - return new Nodes.AssocNode(loadNode(), loadOptionalNode(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.AssocNode(loadNode(), loadOptionalNode(), loadOptionalLocation(), startOffset, length); case 8: - return new Nodes.AssocSplatNode(loadOptionalNode(), loadLocation(), startOffset, endOffset); + return new Nodes.AssocSplatNode(loadOptionalNode(), loadLocation(), startOffset, length); case 9: - return new Nodes.BeginNode(loadOptionalToken(), (Nodes.StatementsNode) loadOptionalNode(), (Nodes.RescueNode) loadOptionalNode(), (Nodes.ElseNode) loadOptionalNode(), (Nodes.EnsureNode) loadOptionalNode(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.BackReferenceReadNode(startOffset, length); case 10: - return new Nodes.BlockArgumentNode(loadOptionalNode(), loadLocation(), startOffset, endOffset); + return new Nodes.BeginNode(loadOptionalLocation(), (Nodes.StatementsNode) loadOptionalNode(), (Nodes.RescueNode) loadOptionalNode(), (Nodes.ElseNode) loadOptionalNode(), (Nodes.EnsureNode) loadOptionalNode(), loadOptionalLocation(), startOffset, length); case 11: - return new Nodes.BlockNode(loadTokens(), (Nodes.BlockParametersNode) loadOptionalNode(), loadOptionalNode(), loadLocation(), loadLocation(), startOffset, endOffset); + return new Nodes.BlockArgumentNode(loadOptionalNode(), loadLocation(), startOffset, length); case 12: - return new Nodes.BlockParameterNode(loadOptionalToken(), loadLocation(), startOffset, endOffset); + return new Nodes.BlockNode(loadConstants(), (Nodes.BlockParametersNode) loadOptionalNode(), loadOptionalNode(), loadLocation(), loadLocation(), startOffset, length); case 13: - return new Nodes.BlockParametersNode((Nodes.ParametersNode) loadOptionalNode(), loadTokens(), loadOptionalLocation(), loadOptionalLocation(), startOffset, endOffset); + return new Nodes.BlockParameterNode(loadOptionalLocation(), loadLocation(), startOffset, length); case 14: - return new Nodes.BreakNode((Nodes.ArgumentsNode) loadOptionalNode(), loadLocation(), startOffset, endOffset); + return new Nodes.BlockParametersNode((Nodes.ParametersNode) loadOptionalNode(), loadLocations(), loadOptionalLocation(), loadOptionalLocation(), startOffset, length); case 15: - return new Nodes.CallNode(loadOptionalNode(), loadOptionalToken(), loadOptionalToken(), loadOptionalToken(), (Nodes.ArgumentsNode) loadOptionalNode(), loadOptionalToken(), (Nodes.BlockNode) loadOptionalNode(), loadString(), startOffset, endOffset); + return new Nodes.BreakNode((Nodes.ArgumentsNode) loadOptionalNode(), loadLocation(), startOffset, length); case 16: - return new Nodes.CapturePatternNode(loadNode(), loadNode(), loadLocation(), startOffset, endOffset); + return new Nodes.CallNode(loadOptionalNode(), loadOptionalLocation(), loadOptionalLocation(), loadOptionalLocation(), (Nodes.ArgumentsNode) loadOptionalNode(), loadOptionalLocation(), (Nodes.BlockNode) loadOptionalNode(), loadVarInt(), loadString(), startOffset, length); case 17: - return new Nodes.CaseNode(loadOptionalNode(), loadNodes(), (Nodes.ElseNode) loadOptionalNode(), loadLocation(), loadLocation(), startOffset, endOffset); + return new Nodes.CallOperatorAndWriteNode((Nodes.CallNode) loadNode(), loadLocation(), loadNode(), startOffset, length); case 18: - return new Nodes.ClassNode(loadTokens(), loadToken(), loadNode(), loadOptionalToken(), loadOptionalNode(), loadOptionalNode(), loadToken(), startOffset, endOffset); + return new Nodes.CallOperatorOrWriteNode((Nodes.CallNode) loadNode(), loadNode(), loadLocation(), startOffset, length); case 19: - return new Nodes.ClassVariableReadNode(startOffset, endOffset); + return new Nodes.CallOperatorWriteNode((Nodes.CallNode) loadNode(), loadLocation(), loadNode(), loadConstant(), startOffset, length); case 20: - return new Nodes.ClassVariableWriteNode(loadLocation(), loadOptionalNode(), loadOptionalLocation(), startOffset, endOffset); + return new Nodes.CapturePatternNode(loadNode(), loadNode(), loadLocation(), startOffset, length); case 21: - return new Nodes.ConstantPathNode(loadOptionalNode(), loadNode(), loadLocation(), startOffset, endOffset); + return new Nodes.CaseNode(loadOptionalNode(), loadNodes(), (Nodes.ElseNode) loadOptionalNode(), loadLocation(), loadLocation(), startOffset, length); case 22: - return new Nodes.ConstantPathWriteNode(loadNode(), loadOptionalToken(), loadOptionalNode(), startOffset, endOffset); + return new Nodes.ClassNode(loadConstants(), loadLocation(), loadNode(), loadOptionalLocation(), loadOptionalNode(), loadOptionalNode(), loadLocation(), startOffset, length); case 23: - return new Nodes.ConstantReadNode(startOffset, endOffset); + return new Nodes.ClassVariableOperatorAndWriteNode(loadLocation(), loadLocation(), loadNode(), startOffset, length); case 24: - return new Nodes.DefNode(buffer.getInt(), loadToken(), loadOptionalNode(), (Nodes.ParametersNode) loadOptionalNode(), loadOptionalNode(), loadTokens(), loadLocation(), loadOptionalLocation(), loadOptionalLocation(), loadOptionalLocation(), loadOptionalLocation(), loadOptionalLocation(), startOffset, endOffset); + return new Nodes.ClassVariableOperatorOrWriteNode(loadLocation(), loadLocation(), loadNode(), startOffset, length); case 25: - return new Nodes.DefinedNode(loadOptionalToken(), loadNode(), loadOptionalToken(), loadLocation(), startOffset, endOffset); + return new Nodes.ClassVariableOperatorWriteNode(loadLocation(), loadLocation(), loadNode(), loadConstant(), startOffset, length); case 26: - return new Nodes.ElseNode(loadToken(), (Nodes.StatementsNode) loadOptionalNode(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.ClassVariableReadNode(startOffset, length); case 27: - return new Nodes.EnsureNode(loadToken(), (Nodes.StatementsNode) loadOptionalNode(), loadToken(), startOffset, endOffset); + return new Nodes.ClassVariableWriteNode(loadLocation(), loadOptionalNode(), loadOptionalLocation(), startOffset, length); case 28: - return new Nodes.FalseNode(startOffset, endOffset); + return new Nodes.ConstantOperatorAndWriteNode(loadLocation(), loadLocation(), loadNode(), startOffset, length); case 29: - return new Nodes.FindPatternNode(loadOptionalNode(), loadNode(), loadNodes(), loadNode(), loadOptionalLocation(), loadOptionalLocation(), startOffset, endOffset); + return new Nodes.ConstantOperatorOrWriteNode(loadLocation(), loadLocation(), loadNode(), startOffset, length); case 30: - return new Nodes.FloatNode(startOffset, endOffset); + return new Nodes.ConstantOperatorWriteNode(loadLocation(), loadLocation(), loadNode(), loadConstant(), startOffset, length); case 31: - return new Nodes.ForNode(loadNode(), loadNode(), (Nodes.StatementsNode) loadOptionalNode(), loadLocation(), loadLocation(), loadOptionalLocation(), loadLocation(), startOffset, endOffset); + return new Nodes.ConstantPathNode(loadOptionalNode(), loadNode(), loadLocation(), startOffset, length); case 32: - return new Nodes.ForwardingArgumentsNode(startOffset, endOffset); + return new Nodes.ConstantPathOperatorAndWriteNode((Nodes.ConstantPathNode) loadNode(), loadLocation(), loadNode(), startOffset, length); case 33: - return new Nodes.ForwardingParameterNode(startOffset, endOffset); + return new Nodes.ConstantPathOperatorOrWriteNode((Nodes.ConstantPathNode) loadNode(), loadLocation(), loadNode(), startOffset, length); case 34: - return new Nodes.ForwardingSuperNode((Nodes.BlockNode) loadOptionalNode(), startOffset, endOffset); + return new Nodes.ConstantPathOperatorWriteNode((Nodes.ConstantPathNode) loadNode(), loadLocation(), loadNode(), loadConstant(), startOffset, length); case 35: - return new Nodes.GlobalVariableReadNode(loadToken(), startOffset, endOffset); + return new Nodes.ConstantPathWriteNode(loadNode(), loadOptionalLocation(), loadOptionalNode(), startOffset, length); case 36: - return new Nodes.GlobalVariableWriteNode(loadToken(), loadOptionalToken(), loadOptionalNode(), startOffset, endOffset); + return new Nodes.ConstantReadNode(startOffset, length); case 37: - return new Nodes.HashNode(loadToken(), loadNodes(), loadToken(), startOffset, endOffset); + return new Nodes.DefNode(buffer.getInt(), loadLocation(), loadOptionalNode(), (Nodes.ParametersNode) loadOptionalNode(), loadOptionalNode(), loadConstants(), loadLocation(), loadOptionalLocation(), loadOptionalLocation(), loadOptionalLocation(), loadOptionalLocation(), loadOptionalLocation(), startOffset, length); case 38: - return new Nodes.HashPatternNode(loadOptionalNode(), loadNodes(), loadOptionalNode(), loadOptionalLocation(), loadOptionalLocation(), startOffset, endOffset); + return new Nodes.DefinedNode(loadOptionalLocation(), loadNode(), loadOptionalLocation(), loadLocation(), startOffset, length); case 39: - return new Nodes.IfNode(loadToken(), loadNode(), (Nodes.StatementsNode) loadOptionalNode(), loadOptionalNode(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.ElseNode(loadLocation(), (Nodes.StatementsNode) loadOptionalNode(), loadOptionalLocation(), startOffset, length); case 40: - return new Nodes.ImaginaryNode(loadNode(), startOffset, endOffset); + return new Nodes.EmbeddedStatementsNode(loadLocation(), (Nodes.StatementsNode) loadOptionalNode(), loadLocation(), startOffset, length); case 41: - return new Nodes.InNode(loadNode(), (Nodes.StatementsNode) loadOptionalNode(), loadLocation(), loadOptionalLocation(), startOffset, endOffset); + return new Nodes.EmbeddedVariableNode(loadLocation(), loadNode(), startOffset, length); case 42: - return new Nodes.InstanceVariableReadNode(startOffset, endOffset); + return new Nodes.EnsureNode(loadLocation(), (Nodes.StatementsNode) loadOptionalNode(), loadLocation(), startOffset, length); case 43: - return new Nodes.InstanceVariableWriteNode(loadLocation(), loadOptionalNode(), loadOptionalLocation(), startOffset, endOffset); + return new Nodes.FalseNode(startOffset, length); case 44: - return new Nodes.IntegerNode(startOffset, endOffset); + return new Nodes.FindPatternNode(loadOptionalNode(), loadNode(), loadNodes(), loadNode(), loadOptionalLocation(), loadOptionalLocation(), startOffset, length); case 45: - return new Nodes.InterpolatedRegularExpressionNode(loadToken(), loadNodes(), loadToken(), startOffset, endOffset); + return new Nodes.FloatNode(startOffset, length); case 46: - return new Nodes.InterpolatedStringNode(loadOptionalToken(), loadNodes(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.ForNode(loadNode(), loadNode(), (Nodes.StatementsNode) loadOptionalNode(), loadLocation(), loadLocation(), loadOptionalLocation(), loadLocation(), startOffset, length); case 47: - return new Nodes.InterpolatedSymbolNode(loadOptionalToken(), loadNodes(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.ForwardingArgumentsNode(startOffset, length); case 48: - return new Nodes.InterpolatedXStringNode(loadToken(), loadNodes(), loadToken(), startOffset, endOffset); + return new Nodes.ForwardingParameterNode(startOffset, length); case 49: - return new Nodes.KeywordHashNode(loadNodes(), startOffset, endOffset); + return new Nodes.ForwardingSuperNode((Nodes.BlockNode) loadOptionalNode(), startOffset, length); case 50: - return new Nodes.KeywordParameterNode(loadToken(), loadOptionalNode(), startOffset, endOffset); + return new Nodes.GlobalVariableOperatorAndWriteNode(loadLocation(), loadLocation(), loadNode(), startOffset, length); case 51: - return new Nodes.KeywordRestParameterNode(loadToken(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.GlobalVariableOperatorOrWriteNode(loadLocation(), loadLocation(), loadNode(), startOffset, length); case 52: - return new Nodes.LambdaNode(loadTokens(), loadToken(), (Nodes.BlockParametersNode) loadOptionalNode(), loadOptionalNode(), startOffset, endOffset); + return new Nodes.GlobalVariableOperatorWriteNode(loadLocation(), loadLocation(), loadNode(), loadConstant(), startOffset, length); case 53: - return new Nodes.LocalVariableReadNode(loadVarInt(), startOffset, endOffset); + return new Nodes.GlobalVariableReadNode(startOffset, length); case 54: - return new Nodes.LocalVariableWriteNode(loadLocation(), loadOptionalNode(), loadOptionalLocation(), loadVarInt(), startOffset, endOffset); + return new Nodes.GlobalVariableWriteNode(loadLocation(), loadOptionalLocation(), loadOptionalNode(), startOffset, length); case 55: - return new Nodes.MatchPredicateNode(loadNode(), loadNode(), loadLocation(), startOffset, endOffset); + return new Nodes.HashNode(loadLocation(), loadNodes(), loadLocation(), startOffset, length); case 56: - return new Nodes.MatchRequiredNode(loadNode(), loadNode(), loadLocation(), startOffset, endOffset); + return new Nodes.HashPatternNode(loadOptionalNode(), loadNodes(), loadOptionalNode(), loadOptionalLocation(), loadOptionalLocation(), startOffset, length); case 57: - return new Nodes.MissingNode(startOffset, endOffset); + return new Nodes.IfNode(loadOptionalLocation(), loadNode(), (Nodes.StatementsNode) loadOptionalNode(), loadOptionalNode(), loadOptionalLocation(), startOffset, length); case 58: - return new Nodes.ModuleNode(loadTokens(), loadToken(), loadNode(), loadOptionalNode(), loadToken(), startOffset, endOffset); + return new Nodes.ImaginaryNode(loadNode(), startOffset, length); case 59: - return new Nodes.MultiWriteNode(loadNodes(), loadOptionalToken(), loadOptionalNode(), loadOptionalLocation(), loadOptionalLocation(), startOffset, endOffset); + return new Nodes.InNode(loadNode(), (Nodes.StatementsNode) loadOptionalNode(), loadLocation(), loadOptionalLocation(), startOffset, length); case 60: - return new Nodes.NextNode((Nodes.ArgumentsNode) loadOptionalNode(), loadLocation(), startOffset, endOffset); + return new Nodes.InstanceVariableOperatorAndWriteNode(loadLocation(), loadLocation(), loadNode(), startOffset, length); case 61: - return new Nodes.NilNode(startOffset, endOffset); + return new Nodes.InstanceVariableOperatorOrWriteNode(loadLocation(), loadLocation(), loadNode(), startOffset, length); case 62: - return new Nodes.NoKeywordsParameterNode(loadLocation(), loadLocation(), startOffset, endOffset); + return new Nodes.InstanceVariableOperatorWriteNode(loadLocation(), loadLocation(), loadNode(), loadConstant(), startOffset, length); case 63: - return new Nodes.OperatorAndAssignmentNode(loadNode(), loadNode(), loadLocation(), startOffset, endOffset); + return new Nodes.InstanceVariableReadNode(startOffset, length); case 64: - return new Nodes.OperatorAssignmentNode(loadNode(), loadToken(), loadNode(), startOffset, endOffset); + return new Nodes.InstanceVariableWriteNode(loadLocation(), loadOptionalNode(), loadOptionalLocation(), startOffset, length); case 65: - return new Nodes.OperatorOrAssignmentNode(loadNode(), loadNode(), loadLocation(), startOffset, endOffset); + return new Nodes.IntegerNode(startOffset, length); case 66: - return new Nodes.OptionalParameterNode(loadToken(), loadToken(), loadNode(), startOffset, endOffset); + return new Nodes.InterpolatedRegularExpressionNode(loadLocation(), loadNodes(), loadLocation(), loadVarInt(), startOffset, length); case 67: - return new Nodes.OrNode(loadNode(), loadNode(), loadLocation(), startOffset, endOffset); + return new Nodes.InterpolatedStringNode(loadOptionalLocation(), loadNodes(), loadOptionalLocation(), startOffset, length); case 68: - return new Nodes.ParametersNode(loadNodes(), loadNodes(), loadNodes(), (Nodes.RestParameterNode) loadOptionalNode(), loadNodes(), loadOptionalNode(), (Nodes.BlockParameterNode) loadOptionalNode(), startOffset, endOffset); + return new Nodes.InterpolatedSymbolNode(loadOptionalLocation(), loadNodes(), loadOptionalLocation(), startOffset, length); case 69: - return new Nodes.ParenthesesNode(loadOptionalNode(), loadLocation(), loadLocation(), startOffset, endOffset); + return new Nodes.InterpolatedXStringNode(loadLocation(), loadNodes(), loadLocation(), startOffset, length); case 70: - return new Nodes.PinnedExpressionNode(loadNode(), loadLocation(), loadLocation(), loadLocation(), startOffset, endOffset); + return new Nodes.KeywordHashNode(loadNodes(), startOffset, length); case 71: - return new Nodes.PinnedVariableNode(loadNode(), loadLocation(), startOffset, endOffset); + return new Nodes.KeywordParameterNode(loadLocation(), loadOptionalNode(), startOffset, length); case 72: - return new Nodes.PostExecutionNode((Nodes.StatementsNode) loadNode(), loadLocation(), loadLocation(), loadLocation(), startOffset, endOffset); + return new Nodes.KeywordRestParameterNode(loadLocation(), loadOptionalLocation(), startOffset, length); case 73: - return new Nodes.PreExecutionNode((Nodes.StatementsNode) loadNode(), loadLocation(), loadLocation(), loadLocation(), startOffset, endOffset); + return new Nodes.LambdaNode(loadConstants(), loadLocation(), (Nodes.BlockParametersNode) loadOptionalNode(), loadOptionalNode(), startOffset, length); case 74: - return new Nodes.ProgramNode(loadTokens(), (Nodes.StatementsNode) loadNode(), startOffset, endOffset); + return new Nodes.LocalVariableOperatorAndWriteNode(loadLocation(), loadLocation(), loadNode(), loadConstant(), startOffset, length); case 75: - return new Nodes.RangeNode(loadOptionalNode(), loadOptionalNode(), loadLocation(), startOffset, endOffset); + return new Nodes.LocalVariableOperatorOrWriteNode(loadLocation(), loadLocation(), loadNode(), loadConstant(), startOffset, length); case 76: - return new Nodes.RationalNode(loadNode(), startOffset, endOffset); + return new Nodes.LocalVariableOperatorWriteNode(loadLocation(), loadLocation(), loadNode(), loadConstant(), loadConstant(), startOffset, length); case 77: - return new Nodes.RedoNode(startOffset, endOffset); + return new Nodes.LocalVariableReadNode(loadConstant(), loadVarInt(), startOffset, length); case 78: - return new Nodes.RegularExpressionNode(loadToken(), loadToken(), loadToken(), loadString(), startOffset, endOffset); + return new Nodes.LocalVariableWriteNode(loadConstant(), loadVarInt(), loadOptionalNode(), loadLocation(), loadOptionalLocation(), startOffset, length); case 79: - return new Nodes.RequiredDestructuredParameterNode(loadNodes(), loadToken(), loadToken(), startOffset, endOffset); + return new Nodes.MatchPredicateNode(loadNode(), loadNode(), loadLocation(), startOffset, length); case 80: - return new Nodes.RequiredParameterNode(startOffset, endOffset); + return new Nodes.MatchRequiredNode(loadNode(), loadNode(), loadLocation(), startOffset, length); case 81: - return new Nodes.RescueModifierNode(loadNode(), loadToken(), loadNode(), startOffset, endOffset); + return new Nodes.MissingNode(startOffset, length); case 82: - return new Nodes.RescueNode(loadToken(), loadNodes(), loadOptionalToken(), loadOptionalNode(), (Nodes.StatementsNode) loadOptionalNode(), (Nodes.RescueNode) loadOptionalNode(), startOffset, endOffset); + return new Nodes.ModuleNode(loadConstants(), loadLocation(), loadNode(), loadOptionalNode(), loadLocation(), startOffset, length); case 83: - return new Nodes.RestParameterNode(loadToken(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.MultiWriteNode(loadNodes(), loadOptionalLocation(), loadOptionalNode(), loadOptionalLocation(), loadOptionalLocation(), startOffset, length); case 84: - return new Nodes.RetryNode(startOffset, endOffset); + return new Nodes.NextNode((Nodes.ArgumentsNode) loadOptionalNode(), loadLocation(), startOffset, length); case 85: - return new Nodes.ReturnNode(loadToken(), (Nodes.ArgumentsNode) loadOptionalNode(), startOffset, endOffset); + return new Nodes.NilNode(startOffset, length); case 86: - return new Nodes.SelfNode(startOffset, endOffset); + return new Nodes.NoKeywordsParameterNode(loadLocation(), loadLocation(), startOffset, length); case 87: - return new Nodes.SingletonClassNode(loadTokens(), loadToken(), loadToken(), loadNode(), loadOptionalNode(), loadToken(), startOffset, endOffset); + return new Nodes.NumberedReferenceReadNode(startOffset, length); case 88: - return new Nodes.SourceEncodingNode(startOffset, endOffset); + return new Nodes.OptionalParameterNode(loadConstant(), loadLocation(), loadLocation(), loadNode(), startOffset, length); case 89: - return new Nodes.SourceFileNode(loadString(), startOffset, endOffset); + return new Nodes.OrNode(loadNode(), loadNode(), loadLocation(), startOffset, length); case 90: - return new Nodes.SourceLineNode(startOffset, endOffset); + return new Nodes.ParametersNode(loadNodes(), loadNodes(), loadNodes(), (Nodes.RestParameterNode) loadOptionalNode(), loadNodes(), loadOptionalNode(), (Nodes.BlockParameterNode) loadOptionalNode(), startOffset, length); case 91: - return new Nodes.SplatNode(loadToken(), loadOptionalNode(), startOffset, endOffset); + return new Nodes.ParenthesesNode(loadOptionalNode(), loadLocation(), loadLocation(), startOffset, length); case 92: - return new Nodes.StatementsNode(loadNodes(), startOffset, endOffset); + return new Nodes.PinnedExpressionNode(loadNode(), loadLocation(), loadLocation(), loadLocation(), startOffset, length); case 93: - return new Nodes.StringConcatNode(loadNode(), loadNode(), startOffset, endOffset); + return new Nodes.PinnedVariableNode(loadNode(), loadLocation(), startOffset, length); case 94: - return new Nodes.StringInterpolatedNode(loadToken(), (Nodes.StatementsNode) loadOptionalNode(), loadToken(), startOffset, endOffset); + return new Nodes.PostExecutionNode((Nodes.StatementsNode) loadOptionalNode(), loadLocation(), loadLocation(), loadLocation(), startOffset, length); case 95: - return new Nodes.StringNode(loadOptionalToken(), loadToken(), loadOptionalToken(), loadString(), startOffset, endOffset); + return new Nodes.PreExecutionNode((Nodes.StatementsNode) loadOptionalNode(), loadLocation(), loadLocation(), loadLocation(), startOffset, length); case 96: - return new Nodes.SuperNode(loadToken(), loadOptionalToken(), (Nodes.ArgumentsNode) loadOptionalNode(), loadOptionalToken(), (Nodes.BlockNode) loadOptionalNode(), startOffset, endOffset); + return new Nodes.ProgramNode(loadConstants(), (Nodes.StatementsNode) loadNode(), startOffset, length); case 97: - return new Nodes.SymbolNode(loadOptionalToken(), loadToken(), loadOptionalToken(), loadString(), startOffset, endOffset); + return new Nodes.RangeNode(loadOptionalNode(), loadOptionalNode(), loadLocation(), loadVarInt(), startOffset, length); case 98: - return new Nodes.TrueNode(startOffset, endOffset); + return new Nodes.RationalNode(loadNode(), startOffset, length); case 99: - return new Nodes.UndefNode(loadNodes(), loadLocation(), startOffset, endOffset); + return new Nodes.RedoNode(startOffset, length); case 100: - return new Nodes.UnlessNode(loadToken(), loadNode(), (Nodes.StatementsNode) loadOptionalNode(), (Nodes.ElseNode) loadOptionalNode(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.RegularExpressionNode(loadLocation(), loadLocation(), loadLocation(), loadString(), loadVarInt(), startOffset, length); case 101: - return new Nodes.UntilNode(loadToken(), loadNode(), (Nodes.StatementsNode) loadOptionalNode(), startOffset, endOffset); + return new Nodes.RequiredDestructuredParameterNode(loadNodes(), loadLocation(), loadLocation(), startOffset, length); case 102: - return new Nodes.WhenNode(loadToken(), loadNodes(), (Nodes.StatementsNode) loadOptionalNode(), startOffset, endOffset); + return new Nodes.RequiredParameterNode(loadConstant(), startOffset, length); case 103: - return new Nodes.WhileNode(loadToken(), loadNode(), (Nodes.StatementsNode) loadOptionalNode(), startOffset, endOffset); + return new Nodes.RescueModifierNode(loadNode(), loadLocation(), loadNode(), startOffset, length); case 104: - return new Nodes.XStringNode(loadToken(), loadToken(), loadToken(), loadString(), startOffset, endOffset); + return new Nodes.RescueNode(loadLocation(), loadNodes(), loadOptionalLocation(), loadOptionalNode(), (Nodes.StatementsNode) loadOptionalNode(), (Nodes.RescueNode) loadOptionalNode(), startOffset, length); case 105: - return new Nodes.YieldNode(loadToken(), loadOptionalToken(), (Nodes.ArgumentsNode) loadOptionalNode(), loadOptionalToken(), startOffset, endOffset); + return new Nodes.RestParameterNode(loadLocation(), loadOptionalLocation(), startOffset, length); + case 106: + return new Nodes.RetryNode(startOffset, length); + case 107: + return new Nodes.ReturnNode(loadLocation(), (Nodes.ArgumentsNode) loadOptionalNode(), startOffset, length); + case 108: + return new Nodes.SelfNode(startOffset, length); + case 109: + return new Nodes.SingletonClassNode(loadConstants(), loadLocation(), loadLocation(), loadNode(), loadOptionalNode(), loadLocation(), startOffset, length); + case 110: + return new Nodes.SourceEncodingNode(startOffset, length); + case 111: + return new Nodes.SourceFileNode(loadString(), startOffset, length); + case 112: + return new Nodes.SourceLineNode(startOffset, length); + case 113: + return new Nodes.SplatNode(loadLocation(), loadOptionalNode(), startOffset, length); + case 114: + return new Nodes.StatementsNode(loadNodes(), startOffset, length); + case 115: + return new Nodes.StringConcatNode(loadNode(), loadNode(), startOffset, length); + case 116: + return new Nodes.StringNode(loadOptionalLocation(), loadLocation(), loadOptionalLocation(), loadString(), startOffset, length); + case 117: + return new Nodes.SuperNode(loadLocation(), loadOptionalLocation(), (Nodes.ArgumentsNode) loadOptionalNode(), loadOptionalLocation(), (Nodes.BlockNode) loadOptionalNode(), startOffset, length); + case 118: + return new Nodes.SymbolNode(loadOptionalLocation(), loadLocation(), loadOptionalLocation(), loadString(), startOffset, length); + case 119: + return new Nodes.TrueNode(startOffset, length); + case 120: + return new Nodes.UndefNode(loadNodes(), loadLocation(), startOffset, length); + case 121: + return new Nodes.UnlessNode(loadLocation(), loadNode(), (Nodes.StatementsNode) loadOptionalNode(), (Nodes.ElseNode) loadOptionalNode(), loadOptionalLocation(), startOffset, length); + case 122: + return new Nodes.UntilNode(loadLocation(), loadNode(), (Nodes.StatementsNode) loadOptionalNode(), startOffset, length); + case 123: + return new Nodes.WhenNode(loadLocation(), loadNodes(), (Nodes.StatementsNode) loadOptionalNode(), startOffset, length); + case 124: + return new Nodes.WhileNode(loadLocation(), loadNode(), (Nodes.StatementsNode) loadOptionalNode(), startOffset, length); + case 125: + return new Nodes.XStringNode(loadLocation(), loadLocation(), loadLocation(), loadString(), startOffset, length); + case 126: + return new Nodes.YieldNode(loadLocation(), loadOptionalLocation(), (Nodes.ArgumentsNode) loadOptionalNode(), loadOptionalLocation(), startOffset, length); default: throw new Error("Unknown node type: " + type); } diff --git a/src/yarp/java/org/yarp/Nodes.java b/src/yarp/java/org/yarp/Nodes.java index a4eebd3294e1..f5e7a694ea78 100644 --- a/src/yarp/java/org/yarp/Nodes.java +++ b/src/yarp/java/org/yarp/Nodes.java @@ -1,7 +1,7 @@ /******************************************************************************/ /* This file is generated by the bin/template script and should not be */ /* modified manually. See */ -/* bin/templates/java/org/yarp/Nodes.java.erb */ +/* templates/java/org/yarp/Nodes.java.erb */ /* if you are looking to modify the */ /* template */ /******************************************************************************/ @@ -17,205 +17,219 @@ // @formatter:off public abstract class Nodes { - public enum TokenType { - OPTIONAL_TOKEN, - EOF, - MISSING, - NOT_PROVIDED, - AMPERSAND, - AMPERSAND_AMPERSAND, - AMPERSAND_AMPERSAND_EQUAL, - AMPERSAND_DOT, - AMPERSAND_EQUAL, - BACKTICK, - BACK_REFERENCE, - BANG, - BANG_EQUAL, - BANG_TILDE, - BRACE_LEFT, - BRACE_RIGHT, - BRACKET_LEFT, - BRACKET_LEFT_ARRAY, - BRACKET_LEFT_RIGHT, - BRACKET_LEFT_RIGHT_EQUAL, - BRACKET_RIGHT, - CARET, - CARET_EQUAL, - CHARACTER_LITERAL, - CLASS_VARIABLE, - COLON, - COLON_COLON, - COMMA, - COMMENT, - CONSTANT, - DOT, - DOT_DOT, - DOT_DOT_DOT, - EMBDOC_BEGIN, - EMBDOC_END, - EMBDOC_LINE, - EMBEXPR_BEGIN, - EMBEXPR_END, - EMBVAR, - EQUAL, - EQUAL_EQUAL, - EQUAL_EQUAL_EQUAL, - EQUAL_GREATER, - EQUAL_TILDE, - FLOAT, - GLOBAL_VARIABLE, - GREATER, - GREATER_EQUAL, - GREATER_GREATER, - GREATER_GREATER_EQUAL, - HEREDOC_END, - HEREDOC_START, - IDENTIFIER, - IGNORED_NEWLINE, - IMAGINARY_NUMBER, - INSTANCE_VARIABLE, - INTEGER, - KEYWORD_ALIAS, - KEYWORD_AND, - KEYWORD_BEGIN, - KEYWORD_BEGIN_UPCASE, - KEYWORD_BREAK, - KEYWORD_CASE, - KEYWORD_CLASS, - KEYWORD_DEF, - KEYWORD_DEFINED, - KEYWORD_DO, - KEYWORD_DO_LOOP, - KEYWORD_ELSE, - KEYWORD_ELSIF, - KEYWORD_END, - KEYWORD_END_UPCASE, - KEYWORD_ENSURE, - KEYWORD_FALSE, - KEYWORD_FOR, - KEYWORD_IF, - KEYWORD_IF_MODIFIER, - KEYWORD_IN, - KEYWORD_MODULE, - KEYWORD_NEXT, - KEYWORD_NIL, - KEYWORD_NOT, - KEYWORD_OR, - KEYWORD_REDO, - KEYWORD_RESCUE, - KEYWORD_RESCUE_MODIFIER, - KEYWORD_RETRY, - KEYWORD_RETURN, - KEYWORD_SELF, - KEYWORD_SUPER, - KEYWORD_THEN, - KEYWORD_TRUE, - KEYWORD_UNDEF, - KEYWORD_UNLESS, - KEYWORD_UNLESS_MODIFIER, - KEYWORD_UNTIL, - KEYWORD_UNTIL_MODIFIER, - KEYWORD_WHEN, - KEYWORD_WHILE, - KEYWORD_WHILE_MODIFIER, - KEYWORD_YIELD, - KEYWORD___ENCODING__, - KEYWORD___FILE__, - KEYWORD___LINE__, - LABEL, - LABEL_END, - LAMBDA_BEGIN, - LESS, - LESS_EQUAL, - LESS_EQUAL_GREATER, - LESS_LESS, - LESS_LESS_EQUAL, - MINUS, - MINUS_EQUAL, - MINUS_GREATER, - NEWLINE, - NTH_REFERENCE, - PARENTHESIS_LEFT, - PARENTHESIS_LEFT_PARENTHESES, - PARENTHESIS_RIGHT, - PERCENT, - PERCENT_EQUAL, - PERCENT_LOWER_I, - PERCENT_LOWER_W, - PERCENT_LOWER_X, - PERCENT_UPPER_I, - PERCENT_UPPER_W, - PIPE, - PIPE_EQUAL, - PIPE_PIPE, - PIPE_PIPE_EQUAL, - PLUS, - PLUS_EQUAL, - QUESTION_MARK, - RATIONAL_NUMBER, - REGEXP_BEGIN, - REGEXP_END, - SEMICOLON, - SLASH, - SLASH_EQUAL, - STAR, - STAR_EQUAL, - STAR_STAR, - STAR_STAR_EQUAL, - STRING_BEGIN, - STRING_CONTENT, - STRING_END, - SYMBOL_BEGIN, - TILDE, - UCOLON_COLON, - UDOT_DOT, - UDOT_DOT_DOT, - UMINUS, - UMINUS_NUM, - UPLUS, - USTAR, - USTAR_STAR, - WORDS_SEP, - __END__, - } - - static final TokenType[] TOKEN_TYPES = TokenType.values(); - - public static final class Token { - public final TokenType type; - public final int startOffset; - public final int endOffset; + public static final class CallNodeFlags implements Comparable { - public Token(TokenType type, int startOffset, int endOffset) { - this.type = type; - this.startOffset = startOffset; - this.endOffset = endOffset; + // &. operator + public static final int SAFE_NAVIGATION = 1 << 0; + + public static boolean isSafeNavigation(int flags) { + return (flags & SAFE_NAVIGATION) != 0; } - public int length() { - return endOffset - startOffset; + private final int flags; + + public CallNodeFlags(int flags) { + this.flags = flags; } - public int endOffset() { - return endOffset; + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof CallNodeFlags)) { + return false; + } + + return flags == ((CallNodeFlags) other).flags; + } + + @Override + public int compareTo(CallNodeFlags other) { + return flags - other.flags; + } + + public boolean isSafeNavigation() { + return (flags & SAFE_NAVIGATION) != 0; + } + + } + + public static final class RangeNodeFlags implements Comparable { + + // ... operator + public static final int EXCLUDE_END = 1 << 0; + + public static boolean isExcludeEnd(int flags) { + return (flags & EXCLUDE_END) != 0; + } + + private final int flags; + + public RangeNodeFlags(int flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof RangeNodeFlags)) { + return false; + } + + return flags == ((RangeNodeFlags) other).flags; + } + + @Override + public int compareTo(RangeNodeFlags other) { + return flags - other.flags; + } + + public boolean isExcludeEnd() { + return (flags & EXCLUDE_END) != 0; + } + + } + + public static final class RegularExpressionFlags implements Comparable { + + // i - ignores the case of characters when matching + public static final int IGNORE_CASE = 1 << 0; + + // m - allows $ to match the end of lines within strings + public static final int MULTI_LINE = 1 << 1; + + // x - ignores whitespace and allows comments in regular expressions + public static final int EXTENDED = 1 << 2; + + // e - forces the EUC-JP encoding + public static final int EUC_JP = 1 << 3; + + // n - forces the ASCII-8BIT encoding + public static final int ASCII_8BIT = 1 << 4; + + // s - forces the Windows-31J encoding + public static final int WINDOWS_31J = 1 << 5; + + // u - forces the UTF-8 encoding + public static final int UTF_8 = 1 << 6; + + // o - only interpolates values into the regular expression once + public static final int ONCE = 1 << 7; + + public static boolean isIgnoreCase(int flags) { + return (flags & IGNORE_CASE) != 0; + } + + public static boolean isMultiLine(int flags) { + return (flags & MULTI_LINE) != 0; + } + + public static boolean isExtended(int flags) { + return (flags & EXTENDED) != 0; + } + + public static boolean isEucJp(int flags) { + return (flags & EUC_JP) != 0; + } + + public static boolean isAscii8bit(int flags) { + return (flags & ASCII_8BIT) != 0; + } + + public static boolean isWindows31j(int flags) { + return (flags & WINDOWS_31J) != 0; + } + + public static boolean isUtf8(int flags) { + return (flags & UTF_8) != 0; } + + public static boolean isOnce(int flags) { + return (flags & ONCE) != 0; + } + + private final int flags; + + public RegularExpressionFlags(int flags) { + this.flags = flags; + } + + @Override + public int hashCode() { + return flags; + } + + @Override + public boolean equals(Object other) { + if (!(other instanceof RegularExpressionFlags)) { + return false; + } + + return flags == ((RegularExpressionFlags) other).flags; + } + + @Override + public int compareTo(RegularExpressionFlags other) { + return flags - other.flags; + } + + public boolean isIgnoreCase() { + return (flags & IGNORE_CASE) != 0; + } + + public boolean isMultiLine() { + return (flags & MULTI_LINE) != 0; + } + + public boolean isExtended() { + return (flags & EXTENDED) != 0; + } + + public boolean isEucJp() { + return (flags & EUC_JP) != 0; + } + + public boolean isAscii8bit() { + return (flags & ASCII_8BIT) != 0; + } + + public boolean isWindows31j() { + return (flags & WINDOWS_31J) != 0; + } + + public boolean isUtf8() { + return (flags & UTF_8) != 0; + } + + public boolean isOnce() { + return (flags & ONCE) != 0; + } + } + public static final class Location { public final int startOffset; - public final int endOffset; + public final int length; - public Location(int startOffset, int endOffset) { + public Location(int startOffset, int length) { this.startOffset = startOffset; - this.endOffset = endOffset; + this.length = length; } public int length() { - return endOffset - startOffset; + return length; } public int endOffset() { - return endOffset; + return startOffset + length; } } @@ -224,19 +238,19 @@ public static abstract class Node { public static final Node[] EMPTY_ARRAY = {}; public final int startOffset; - public final int endOffset; + public final int length; - public Node(int startOffset, int endOffset) { + public Node(int startOffset, int length) { this.startOffset = startOffset; - this.endOffset = endOffset; + this.length = length; } public int length() { - return endOffset - startOffset; + return length; } public int endOffset() { - return endOffset; + return startOffset + length; } public abstract T accept(AbstractNodeVisitor visitor); @@ -270,8 +284,8 @@ public static final class AliasNode extends Node { public final Node old_name; public final Location keyword_loc; - public AliasNode(Node new_name, Node old_name, Location keyword_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public AliasNode(Node new_name, Node old_name, Location keyword_loc, int startOffset, int length) { + super(startOffset, length); this.new_name = new_name; this.old_name = old_name; this.keyword_loc = keyword_loc; @@ -295,8 +309,8 @@ public static final class AlternationPatternNode extends Node { public final Node right; public final Location operator_loc; - public AlternationPatternNode(Node left, Node right, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public AlternationPatternNode(Node left, Node right, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.left = left; this.right = right; this.operator_loc = operator_loc; @@ -318,13 +332,13 @@ public T accept(AbstractNodeVisitor visitor) { public static final class AndNode extends Node { public final Node left; public final Node right; - public final Token operator; + public final Location operator_loc; - public AndNode(Node left, Node right, Token operator, int startOffset, int endOffset) { - super(startOffset, endOffset); + public AndNode(Node left, Node right, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.left = left; this.right = right; - this.operator = operator; + this.operator_loc = operator_loc; } public Node[] childNodes() { @@ -343,8 +357,8 @@ public T accept(AbstractNodeVisitor visitor) { public static final class ArgumentsNode extends Node { public final Node[] arguments; - public ArgumentsNode(Node[] arguments, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ArgumentsNode(Node[] arguments, int startOffset, int length) { + super(startOffset, length); this.arguments = arguments; } @@ -364,14 +378,14 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^^^^^ public static final class ArrayNode extends Node { public final Node[] elements; - public final Token opening; // optional - public final Token closing; // optional + public final Location opening_loc; // optional + public final Location closing_loc; // optional - public ArrayNode(Node[] elements, Token opening, Token closing, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ArrayNode(Node[] elements, Location opening_loc, Location closing_loc, int startOffset, int length) { + super(startOffset, length); this.elements = elements; - this.opening = opening; - this.closing = closing; + this.opening_loc = opening_loc; + this.closing_loc = closing_loc; } public Node[] childNodes() { @@ -407,8 +421,8 @@ public static final class ArrayPatternNode extends Node { public final Location opening_loc; // optional public final Location closing_loc; // optional - public ArrayPatternNode(Node constant, Node[] requireds, Node rest, Node[] posts, Location opening_loc, Location closing_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ArrayPatternNode(Node constant, Node[] requireds, Node rest, Node[] posts, Location opening_loc, Location closing_loc, int startOffset, int length) { + super(startOffset, length); this.constant = constant; this.requireds = requireds; this.rest = rest; @@ -438,13 +452,13 @@ public T accept(AbstractNodeVisitor visitor) { public static final class AssocNode extends Node { public final Node key; public final Node value; // optional - public final Token operator; // optional + public final Location operator_loc; // optional - public AssocNode(Node key, Node value, Token operator, int startOffset, int endOffset) { - super(startOffset, endOffset); + public AssocNode(Node key, Node value, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.key = key; this.value = value; - this.operator = operator; + this.operator_loc = operator_loc; } public Node[] childNodes() { @@ -464,8 +478,8 @@ public static final class AssocSplatNode extends Node { public final Node value; // optional public final Location operator_loc; - public AssocSplatNode(Node value, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public AssocSplatNode(Node value, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.value = value; this.operator_loc = operator_loc; } @@ -479,6 +493,25 @@ public T accept(AbstractNodeVisitor visitor) { } } + // Represents reading a reference to a field in the previous match. + // + // $' + // ^^ + public static final class BackReferenceReadNode extends Node { + + public BackReferenceReadNode(int startOffset, int length) { + super(startOffset, length); + } + + public Node[] childNodes() { + return EMPTY_ARRAY; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitBackReferenceReadNode(this); + } + } + // Represents a begin statement. // // begin @@ -486,21 +519,21 @@ public T accept(AbstractNodeVisitor visitor) { // end // ^^^^^ public static final class BeginNode extends Node { - public final Token begin_keyword; // optional + public final Location begin_keyword_loc; // optional public final StatementsNode statements; // optional public final RescueNode rescue_clause; // optional public final ElseNode else_clause; // optional public final EnsureNode ensure_clause; // optional - public final Token end_keyword; // optional + public final Location end_keyword_loc; // optional - public BeginNode(Token begin_keyword, StatementsNode statements, RescueNode rescue_clause, ElseNode else_clause, EnsureNode ensure_clause, Token end_keyword, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.begin_keyword = begin_keyword; + public BeginNode(Location begin_keyword_loc, StatementsNode statements, RescueNode rescue_clause, ElseNode else_clause, EnsureNode ensure_clause, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); + this.begin_keyword_loc = begin_keyword_loc; this.statements = statements; this.rescue_clause = rescue_clause; this.else_clause = else_clause; this.ensure_clause = ensure_clause; - this.end_keyword = end_keyword; + this.end_keyword_loc = end_keyword_loc; } public Node[] childNodes() { @@ -520,8 +553,8 @@ public static final class BlockArgumentNode extends Node { public final Node expression; // optional public final Location operator_loc; - public BlockArgumentNode(Node expression, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public BlockArgumentNode(Node expression, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.expression = expression; this.operator_loc = operator_loc; } @@ -540,14 +573,14 @@ public T accept(AbstractNodeVisitor visitor) { // [1, 2, 3].each { |i| puts x } // ^^^^^^^^^^^^^^ public static final class BlockNode extends Node { - public final Token[] locals; + public final byte[][] locals; public final BlockParametersNode parameters; // optional public final Node statements; // optional public final Location opening_loc; public final Location closing_loc; - public BlockNode(Token[] locals, BlockParametersNode parameters, Node statements, Location opening_loc, Location closing_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public BlockNode(byte[][] locals, BlockParametersNode parameters, Node statements, Location opening_loc, Location closing_loc, int startOffset, int length) { + super(startOffset, length); this.locals = locals; this.parameters = parameters; this.statements = statements; @@ -570,12 +603,12 @@ public T accept(AbstractNodeVisitor visitor) { // ^^ // end public static final class BlockParameterNode extends Node { - public final Token name; // optional + public final Location name_loc; // optional public final Location operator_loc; - public BlockParameterNode(Token name, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.name = name; + public BlockParameterNode(Location name_loc, Location operator_loc, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; this.operator_loc = operator_loc; } @@ -598,12 +631,12 @@ public T accept(AbstractNodeVisitor visitor) { // end public static final class BlockParametersNode extends Node { public final ParametersNode parameters; // optional - public final Token[] locals; + public final Location[] locals; public final Location opening_loc; // optional public final Location closing_loc; // optional - public BlockParametersNode(ParametersNode parameters, Token[] locals, Location opening_loc, Location closing_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public BlockParametersNode(ParametersNode parameters, Location[] locals, Location opening_loc, Location closing_loc, int startOffset, int length) { + super(startOffset, length); this.parameters = parameters; this.locals = locals; this.opening_loc = opening_loc; @@ -627,8 +660,8 @@ public static final class BreakNode extends Node { public final ArgumentsNode arguments; // optional public final Location keyword_loc; - public BreakNode(ArgumentsNode arguments, Location keyword_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public BreakNode(ArgumentsNode arguments, Location keyword_loc, int startOffset, int length) { + super(startOffset, length); this.arguments = arguments; this.keyword_loc = keyword_loc; } @@ -663,23 +696,25 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^^^^ public static final class CallNode extends Node { public final Node receiver; // optional - public final Token call_operator; // optional - public final Token message; // optional - public final Token opening; // optional + public final Location operator_loc; // optional + public final Location message_loc; // optional + public final Location opening_loc; // optional public final ArgumentsNode arguments; // optional - public final Token closing; // optional + public final Location closing_loc; // optional public final BlockNode block; // optional + public final int flags; public final byte[] name; - public CallNode(Node receiver, Token call_operator, Token message, Token opening, ArgumentsNode arguments, Token closing, BlockNode block, byte[] name, int startOffset, int endOffset) { - super(startOffset, endOffset); + public CallNode(Node receiver, Location operator_loc, Location message_loc, Location opening_loc, ArgumentsNode arguments, Location closing_loc, BlockNode block, int flags, byte[] name, int startOffset, int length) { + super(startOffset, length); this.receiver = receiver; - this.call_operator = call_operator; - this.message = message; - this.opening = opening; + this.operator_loc = operator_loc; + this.message_loc = message_loc; + this.opening_loc = opening_loc; this.arguments = arguments; - this.closing = closing; + this.closing_loc = closing_loc; this.block = block; + this.flags = flags; this.name = name; } @@ -692,6 +727,83 @@ public T accept(AbstractNodeVisitor visitor) { } } + // Represents the use of the `&&=` operator on a call. + // + // foo.bar &&= value + // ^^^^^^^^^^^^^^^^^ + public static final class CallOperatorAndWriteNode extends Node { + public final CallNode target; + public final Location operator_loc; + public final Node value; + + public CallOperatorAndWriteNode(CallNode target, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.target = target; + this.operator_loc = operator_loc; + this.value = value; + } + + public Node[] childNodes() { + return new Node[] { target, value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCallOperatorAndWriteNode(this); + } + } + + // Represents the use of the `||=` operator on a call. + // + // foo.bar ||= value + // ^^^^^^^^^^^^^^^^^ + public static final class CallOperatorOrWriteNode extends Node { + public final CallNode target; + public final Node value; + public final Location operator_loc; + + public CallOperatorOrWriteNode(CallNode target, Node value, Location operator_loc, int startOffset, int length) { + super(startOffset, length); + this.target = target; + this.value = value; + this.operator_loc = operator_loc; + } + + public Node[] childNodes() { + return new Node[] { target, value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCallOperatorOrWriteNode(this); + } + } + + // Represents the use of an assignment operator on a call. + // + // foo.bar += baz + // ^^^^^^^^^^^^^^ + public static final class CallOperatorWriteNode extends Node { + public final CallNode target; + public final Location operator_loc; + public final Node value; + public final byte[] operator_id; + + public CallOperatorWriteNode(CallNode target, Location operator_loc, Node value, byte[] operator_id, int startOffset, int length) { + super(startOffset, length); + this.target = target; + this.operator_loc = operator_loc; + this.value = value; + this.operator_id = operator_id; + } + + public Node[] childNodes() { + return new Node[] { target, value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitCallOperatorWriteNode(this); + } + } + // Represents assigning to a local variable in pattern matching. // // foo => [bar => baz] @@ -701,8 +813,8 @@ public static final class CapturePatternNode extends Node { public final Node target; public final Location operator_loc; - public CapturePatternNode(Node value, Node target, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public CapturePatternNode(Node value, Node target, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.value = value; this.target = target; this.operator_loc = operator_loc; @@ -730,8 +842,8 @@ public static final class CaseNode extends Node { public final Location case_keyword_loc; public final Location end_keyword_loc; - public CaseNode(Node predicate, Node[] conditions, ElseNode consequent, Location case_keyword_loc, Location end_keyword_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public CaseNode(Node predicate, Node[] conditions, ElseNode consequent, Location case_keyword_loc, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); this.predicate = predicate; this.conditions = conditions; this.consequent = consequent; @@ -757,23 +869,23 @@ public T accept(AbstractNodeVisitor visitor) { // class Foo end // ^^^^^^^^^^^^^ public static final class ClassNode extends Node { - public final Token[] locals; - public final Token class_keyword; + public final byte[][] locals; + public final Location class_keyword_loc; public final Node constant_path; - public final Token inheritance_operator; // optional + public final Location inheritance_operator_loc; // optional public final Node superclass; // optional public final Node statements; // optional - public final Token end_keyword; + public final Location end_keyword_loc; - public ClassNode(Token[] locals, Token class_keyword, Node constant_path, Token inheritance_operator, Node superclass, Node statements, Token end_keyword, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ClassNode(byte[][] locals, Location class_keyword_loc, Node constant_path, Location inheritance_operator_loc, Node superclass, Node statements, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); this.locals = locals; - this.class_keyword = class_keyword; + this.class_keyword_loc = class_keyword_loc; this.constant_path = constant_path; - this.inheritance_operator = inheritance_operator; + this.inheritance_operator_loc = inheritance_operator_loc; this.superclass = superclass; this.statements = statements; - this.end_keyword = end_keyword; + this.end_keyword_loc = end_keyword_loc; } public Node[] childNodes() { @@ -785,39 +897,45 @@ public T accept(AbstractNodeVisitor visitor) { } } - // Represents referencing a class variable. + // Represents the use of the `&&=` operator for assignment to a class variable. // - // @@foo - // ^^^^^ - public static final class ClassVariableReadNode extends Node { + // @@target &&= value + // ^^^^^^^^^^^^^^^^ + public static final class ClassVariableOperatorAndWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; - public ClassVariableReadNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public ClassVariableOperatorAndWriteNode(Location name_loc, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; } public Node[] childNodes() { - return EMPTY_ARRAY; + return new Node[] { value }; } public T accept(AbstractNodeVisitor visitor) { - return visitor.visitClassVariableReadNode(this); + return visitor.visitClassVariableOperatorAndWriteNode(this); } } - // Represents writing to a class variable. + // Represents the use of the `||=` operator for assignment to a class variable. // - // @@foo = 1 - // ^^^^^^^^^ - public static final class ClassVariableWriteNode extends Node { + // @@target ||= value + // ^^^^^^^^^^^^^^^^^^ + public static final class ClassVariableOperatorOrWriteNode extends Node { public final Location name_loc; - public final Node value; // optional - public final Location operator_loc; // optional + public final Location operator_loc; + public final Node value; - public ClassVariableWriteNode(Location name_loc, Node value, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ClassVariableOperatorOrWriteNode(Location name_loc, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); this.name_loc = name_loc; - this.value = value; this.operator_loc = operator_loc; + this.value = value; } public Node[] childNodes() { @@ -825,71 +943,296 @@ public Node[] childNodes() { } public T accept(AbstractNodeVisitor visitor) { - return visitor.visitClassVariableWriteNode(this); + return visitor.visitClassVariableOperatorOrWriteNode(this); } } - // Represents accessing a constant through a path of `::` operators. + // Represents assigning to a class variable using an operator that isn't `=`. // - // Foo::Bar - // ^^^^^^^^ - public static final class ConstantPathNode extends Node { - public final Node parent; // optional - public final Node child; - public final Location delimiter_loc; + // @@target += value + // ^^^^^^^^^^^^^^^^^ + public static final class ClassVariableOperatorWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + public final byte[] operator; - public ConstantPathNode(Node parent, Node child, Location delimiter_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.parent = parent; - this.child = child; - this.delimiter_loc = delimiter_loc; + public ClassVariableOperatorWriteNode(Location name_loc, Location operator_loc, Node value, byte[] operator, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + this.operator = operator; } public Node[] childNodes() { - return new Node[] { parent, child }; + return new Node[] { value }; } public T accept(AbstractNodeVisitor visitor) { - return visitor.visitConstantPathNode(this); + return visitor.visitClassVariableOperatorWriteNode(this); } } - // Represents writing to a constant. - // - // Foo = 1 - // ^^^^^^^ + // Represents referencing a class variable. // - // Foo::Bar = 1 - // ^^^^^^^^^^^^ - public static final class ConstantPathWriteNode extends Node { - public final Node target; - public final Token operator; // optional - public final Node value; // optional + // @@foo + // ^^^^^ + public static final class ClassVariableReadNode extends Node { - public ConstantPathWriteNode(Node target, Token operator, Node value, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.target = target; - this.operator = operator; - this.value = value; + public ClassVariableReadNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { - return new Node[] { target, value }; + return EMPTY_ARRAY; } public T accept(AbstractNodeVisitor visitor) { - return visitor.visitConstantPathWriteNode(this); + return visitor.visitClassVariableReadNode(this); } } - // Represents referencing a constant. + // Represents writing to a class variable. // - // Foo - // ^^^ + // @@foo = 1 + // ^^^^^^^^^ + public static final class ClassVariableWriteNode extends Node { + public final Location name_loc; + public final Node value; // optional + public final Location operator_loc; // optional + + public ClassVariableWriteNode(Location name_loc, Node value, Location operator_loc, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.value = value; + this.operator_loc = operator_loc; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitClassVariableWriteNode(this); + } + } + + // Represents the use of the `&&=` operator for assignment to a constant. + // + // Target &&= value + // ^^^^^^^^^^^^^^^^ + public static final class ConstantOperatorAndWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + + public ConstantOperatorAndWriteNode(Location name_loc, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantOperatorAndWriteNode(this); + } + } + + // Represents the use of the `||=` operator for assignment to a constant. + // + // Target ||= value + // ^^^^^^^^^^^^^^^^ + public static final class ConstantOperatorOrWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + + public ConstantOperatorOrWriteNode(Location name_loc, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantOperatorOrWriteNode(this); + } + } + + // Represents assigning to a constant using an operator that isn't `=`. + // + // Target += value + // ^^^^^^^^^^^^^^^ + public static final class ConstantOperatorWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + public final byte[] operator; + + public ConstantOperatorWriteNode(Location name_loc, Location operator_loc, Node value, byte[] operator, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + this.operator = operator; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantOperatorWriteNode(this); + } + } + + // Represents accessing a constant through a path of `::` operators. + // + // Foo::Bar + // ^^^^^^^^ + public static final class ConstantPathNode extends Node { + public final Node parent; // optional + public final Node child; + public final Location delimiter_loc; + + public ConstantPathNode(Node parent, Node child, Location delimiter_loc, int startOffset, int length) { + super(startOffset, length); + this.parent = parent; + this.child = child; + this.delimiter_loc = delimiter_loc; + } + + public Node[] childNodes() { + return new Node[] { parent, child }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathNode(this); + } + } + + // Represents the use of the `&&=` operator for assignment to a constant path. + // + // Parent::Child &&= value + // ^^^^^^^^^^^^^^^^^^^^^^^ + public static final class ConstantPathOperatorAndWriteNode extends Node { + public final ConstantPathNode target; + public final Location operator_loc; + public final Node value; + + public ConstantPathOperatorAndWriteNode(ConstantPathNode target, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.target = target; + this.operator_loc = operator_loc; + this.value = value; + } + + public Node[] childNodes() { + return new Node[] { target, value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathOperatorAndWriteNode(this); + } + } + + // Represents the use of the `||=` operator for assignment to a constant path. + // + // Parent::Child ||= value + // ^^^^^^^^^^^^^^^^^^^^^^^ + public static final class ConstantPathOperatorOrWriteNode extends Node { + public final ConstantPathNode target; + public final Location operator_loc; + public final Node value; + + public ConstantPathOperatorOrWriteNode(ConstantPathNode target, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.target = target; + this.operator_loc = operator_loc; + this.value = value; + } + + public Node[] childNodes() { + return new Node[] { target, value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathOperatorOrWriteNode(this); + } + } + + // Represents assigning to a constant path using an operator that isn't `=`. + // + // Parent::Child += value + // ^^^^^^^^^^^^^^^^^^^^^^ + public static final class ConstantPathOperatorWriteNode extends Node { + public final ConstantPathNode target; + public final Location operator_loc; + public final Node value; + public final byte[] operator; + + public ConstantPathOperatorWriteNode(ConstantPathNode target, Location operator_loc, Node value, byte[] operator, int startOffset, int length) { + super(startOffset, length); + this.target = target; + this.operator_loc = operator_loc; + this.value = value; + this.operator = operator; + } + + public Node[] childNodes() { + return new Node[] { target, value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathOperatorWriteNode(this); + } + } + + // Represents writing to a constant. + // + // Foo = 1 + // ^^^^^^^ + // + // Foo::Bar = 1 + // ^^^^^^^^^^^^ + public static final class ConstantPathWriteNode extends Node { + public final Node target; + public final Location operator_loc; // optional + public final Node value; // optional + + public ConstantPathWriteNode(Node target, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.target = target; + this.operator_loc = operator_loc; + this.value = value; + } + + public Node[] childNodes() { + return new Node[] { target, value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitConstantPathWriteNode(this); + } + } + + // Represents referencing a constant. + // + // Foo + // ^^^ public static final class ConstantReadNode extends Node { - public ConstantReadNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public ConstantReadNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -908,11 +1251,11 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^^^^^^ public static final class DefNode extends Node { public final int serializedLength; - public final Token name; + public final Location name_loc; public final Node receiver; // optional public final ParametersNode parameters; // optional public final Node statements; // optional - public final Token[] locals; + public final byte[][] locals; public final Location def_keyword_loc; public final Location operator_loc; // optional public final Location lparen_loc; // optional @@ -920,10 +1263,10 @@ public static final class DefNode extends Node { public final Location equal_loc; // optional public final Location end_keyword_loc; // optional - public DefNode(int serializedLength, Token name, Node receiver, ParametersNode parameters, Node statements, Token[] locals, Location def_keyword_loc, Location operator_loc, Location lparen_loc, Location rparen_loc, Location equal_loc, Location end_keyword_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public DefNode(int serializedLength, Location name_loc, Node receiver, ParametersNode parameters, Node statements, byte[][] locals, Location def_keyword_loc, Location operator_loc, Location lparen_loc, Location rparen_loc, Location equal_loc, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); this.serializedLength = serializedLength; - this.name = name; + this.name_loc = name_loc; this.receiver = receiver; this.parameters = parameters; this.statements = statements; @@ -950,16 +1293,16 @@ public T accept(AbstractNodeVisitor visitor) { // defined?(a) // ^^^^^^^^^^^ public static final class DefinedNode extends Node { - public final Token lparen; // optional + public final Location lparen_loc; // optional public final Node value; - public final Token rparen; // optional + public final Location rparen_loc; // optional public final Location keyword_loc; - public DefinedNode(Token lparen, Node value, Token rparen, Location keyword_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.lparen = lparen; + public DefinedNode(Location lparen_loc, Node value, Location rparen_loc, Location keyword_loc, int startOffset, int length) { + super(startOffset, length); + this.lparen_loc = lparen_loc; this.value = value; - this.rparen = rparen; + this.rparen_loc = rparen_loc; this.keyword_loc = keyword_loc; } @@ -977,15 +1320,15 @@ public T accept(AbstractNodeVisitor visitor) { // if a then b else c end // ^^^^^^^^^^ public static final class ElseNode extends Node { - public final Token else_keyword; + public final Location else_keyword_loc; public final StatementsNode statements; // optional - public final Token end_keyword; // optional + public final Location end_keyword_loc; // optional - public ElseNode(Token else_keyword, StatementsNode statements, Token end_keyword, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.else_keyword = else_keyword; + public ElseNode(Location else_keyword_loc, StatementsNode statements, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); + this.else_keyword_loc = else_keyword_loc; this.statements = statements; - this.end_keyword = end_keyword; + this.end_keyword_loc = end_keyword_loc; } public Node[] childNodes() { @@ -997,6 +1340,54 @@ public T accept(AbstractNodeVisitor visitor) { } } + // Represents an interpolated set of statements. + // + // "foo #{bar}" + // ^^^^^^ + public static final class EmbeddedStatementsNode extends Node { + public final Location opening_loc; + public final StatementsNode statements; // optional + public final Location closing_loc; + + public EmbeddedStatementsNode(Location opening_loc, StatementsNode statements, Location closing_loc, int startOffset, int length) { + super(startOffset, length); + this.opening_loc = opening_loc; + this.statements = statements; + this.closing_loc = closing_loc; + } + + public Node[] childNodes() { + return new Node[] { statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitEmbeddedStatementsNode(this); + } + } + + // Represents an interpolated variable. + // + // "foo #@bar" + // ^^^^^ + public static final class EmbeddedVariableNode extends Node { + public final Location operator_loc; + public final Node variable; + + public EmbeddedVariableNode(Location operator_loc, Node variable, int startOffset, int length) { + super(startOffset, length); + this.operator_loc = operator_loc; + this.variable = variable; + } + + public Node[] childNodes() { + return new Node[] { variable }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitEmbeddedVariableNode(this); + } + } + // Represents an `ensure` clause in a `begin` statement. // // begin @@ -1006,15 +1397,15 @@ public T accept(AbstractNodeVisitor visitor) { // bar // end public static final class EnsureNode extends Node { - public final Token ensure_keyword; + public final Location ensure_keyword_loc; public final StatementsNode statements; // optional - public final Token end_keyword; + public final Location end_keyword_loc; - public EnsureNode(Token ensure_keyword, StatementsNode statements, Token end_keyword, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.ensure_keyword = ensure_keyword; + public EnsureNode(Location ensure_keyword_loc, StatementsNode statements, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); + this.ensure_keyword_loc = ensure_keyword_loc; this.statements = statements; - this.end_keyword = end_keyword; + this.end_keyword_loc = end_keyword_loc; } public Node[] childNodes() { @@ -1032,8 +1423,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^ public static final class FalseNode extends Node { - public FalseNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public FalseNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -1063,8 +1454,8 @@ public static final class FindPatternNode extends Node { public final Location opening_loc; // optional public final Location closing_loc; // optional - public FindPatternNode(Node constant, Node left, Node[] requireds, Node right, Location opening_loc, Location closing_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public FindPatternNode(Node constant, Node left, Node[] requireds, Node right, Location opening_loc, Location closing_loc, int startOffset, int length) { + super(startOffset, length); this.constant = constant; this.left = left; this.requireds = requireds; @@ -1093,8 +1484,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^ public static final class FloatNode extends Node { - public FloatNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public FloatNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -1119,8 +1510,8 @@ public static final class ForNode extends Node { public final Location do_keyword_loc; // optional public final Location end_keyword_loc; - public ForNode(Node index, Node collection, StatementsNode statements, Location for_keyword_loc, Location in_keyword_loc, Location do_keyword_loc, Location end_keyword_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ForNode(Node index, Node collection, StatementsNode statements, Location for_keyword_loc, Location in_keyword_loc, Location do_keyword_loc, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); this.index = index; this.collection = collection; this.statements = statements; @@ -1147,8 +1538,8 @@ public T accept(AbstractNodeVisitor visitor) { // end public static final class ForwardingArgumentsNode extends Node { - public ForwardingArgumentsNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public ForwardingArgumentsNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -1167,8 +1558,8 @@ public T accept(AbstractNodeVisitor visitor) { // end public static final class ForwardingParameterNode extends Node { - public ForwardingParameterNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public ForwardingParameterNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -1187,8 +1578,8 @@ public T accept(AbstractNodeVisitor visitor) { public static final class ForwardingSuperNode extends Node { public final BlockNode block; // optional - public ForwardingSuperNode(BlockNode block, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ForwardingSuperNode(BlockNode block, int startOffset, int length) { + super(startOffset, length); this.block = block; } @@ -1201,16 +1592,91 @@ public T accept(AbstractNodeVisitor visitor) { } } + // Represents the use of the `&&=` operator for assignment to a global variable. + // + // $target &&= value + // ^^^^^^^^^^^^^^^^^ + public static final class GlobalVariableOperatorAndWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + + public GlobalVariableOperatorAndWriteNode(Location name_loc, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitGlobalVariableOperatorAndWriteNode(this); + } + } + + // Represents the use of the `||=` operator for assignment to a global variable. + // + // $target ||= value + // ^^^^^^^^^^^^^^^^^ + public static final class GlobalVariableOperatorOrWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + + public GlobalVariableOperatorOrWriteNode(Location name_loc, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitGlobalVariableOperatorOrWriteNode(this); + } + } + + // Represents assigning to a global variable using an operator that isn't `=`. + // + // $target += value + // ^^^^^^^^^^^^^^^^ + public static final class GlobalVariableOperatorWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + public final byte[] operator; + + public GlobalVariableOperatorWriteNode(Location name_loc, Location operator_loc, Node value, byte[] operator, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + this.operator = operator; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitGlobalVariableOperatorWriteNode(this); + } + } + // Represents referencing a global variable. // // $foo // ^^^^ public static final class GlobalVariableReadNode extends Node { - public final Token name; - public GlobalVariableReadNode(Token name, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.name = name; + public GlobalVariableReadNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -1227,14 +1693,14 @@ public T accept(AbstractNodeVisitor visitor) { // $foo = 1 // ^^^^^^^^ public static final class GlobalVariableWriteNode extends Node { - public final Token name; - public final Token operator; // optional + public final Location name_loc; + public final Location operator_loc; // optional public final Node value; // optional - public GlobalVariableWriteNode(Token name, Token operator, Node value, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.name = name; - this.operator = operator; + public GlobalVariableWriteNode(Location name_loc, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; this.value = value; } @@ -1252,15 +1718,15 @@ public T accept(AbstractNodeVisitor visitor) { // { a => b } // ^^^^^^^^^^ public static final class HashNode extends Node { - public final Token opening; + public final Location opening_loc; public final Node[] elements; - public final Token closing; + public final Location closing_loc; - public HashNode(Token opening, Node[] elements, Token closing, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.opening = opening; + public HashNode(Location opening_loc, Node[] elements, Location closing_loc, int startOffset, int length) { + super(startOffset, length); + this.opening_loc = opening_loc; this.elements = elements; - this.closing = closing; + this.closing_loc = closing_loc; } public Node[] childNodes() { @@ -1286,8 +1752,8 @@ public static final class HashPatternNode extends Node { public final Location opening_loc; // optional public final Location closing_loc; // optional - public HashPatternNode(Node constant, Node[] assocs, Node kwrest, Location opening_loc, Location closing_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public HashPatternNode(Node constant, Node[] assocs, Node kwrest, Location opening_loc, Location closing_loc, int startOffset, int length) { + super(startOffset, length); this.constant = constant; this.assocs = assocs; this.kwrest = kwrest; @@ -1316,19 +1782,19 @@ public T accept(AbstractNodeVisitor visitor) { // if foo then bar end // ^^^^^^^^^^^^^^^^^^^ public static final class IfNode extends Node { - public final Token if_keyword; + public final Location if_keyword_loc; // optional public final Node predicate; public final StatementsNode statements; // optional public final Node consequent; // optional - public final Token end_keyword; // optional + public final Location end_keyword_loc; // optional - public IfNode(Token if_keyword, Node predicate, StatementsNode statements, Node consequent, Token end_keyword, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.if_keyword = if_keyword; + public IfNode(Location if_keyword_loc, Node predicate, StatementsNode statements, Node consequent, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); + this.if_keyword_loc = if_keyword_loc; this.predicate = predicate; this.statements = statements; this.consequent = consequent; - this.end_keyword = end_keyword; + this.end_keyword_loc = end_keyword_loc; } public Node[] childNodes() { @@ -1347,8 +1813,8 @@ public T accept(AbstractNodeVisitor visitor) { public static final class ImaginaryNode extends Node { public final Node numeric; - public ImaginaryNode(Node numeric, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ImaginaryNode(Node numeric, int startOffset, int length) { + super(startOffset, length); this.numeric = numeric; } @@ -1363,28 +1829,105 @@ public T accept(AbstractNodeVisitor visitor) { // Represents the use of the `in` keyword in a case statement. // - // case a; in b then c end - // ^^^^^^^^^^^ - public static final class InNode extends Node { - public final Node pattern; - public final StatementsNode statements; // optional - public final Location in_loc; - public final Location then_loc; // optional + // case a; in b then c end + // ^^^^^^^^^^^ + public static final class InNode extends Node { + public final Node pattern; + public final StatementsNode statements; // optional + public final Location in_loc; + public final Location then_loc; // optional + + public InNode(Node pattern, StatementsNode statements, Location in_loc, Location then_loc, int startOffset, int length) { + super(startOffset, length); + this.pattern = pattern; + this.statements = statements; + this.in_loc = in_loc; + this.then_loc = then_loc; + } + + public Node[] childNodes() { + return new Node[] { pattern, statements }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInNode(this); + } + } + + // Represents the use of the `&&=` operator for assignment to an instance variable. + // + // @target &&= value + // ^^^^^^^^^^^^^^^^^ + public static final class InstanceVariableOperatorAndWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + + public InstanceVariableOperatorAndWriteNode(Location name_loc, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInstanceVariableOperatorAndWriteNode(this); + } + } + + // Represents the use of the `||=` operator for assignment to an instance variable. + // + // @target ||= value + // ^^^^^^^^^^^^^^^^^ + public static final class InstanceVariableOperatorOrWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + + public InstanceVariableOperatorOrWriteNode(Location name_loc, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitInstanceVariableOperatorOrWriteNode(this); + } + } + + // Represents assigning to an instance variable using an operator that isn't `=`. + // + // @target += value + // ^^^^^^^^^^^^^^^^ + public static final class InstanceVariableOperatorWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + public final byte[] operator; - public InNode(Node pattern, StatementsNode statements, Location in_loc, Location then_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.pattern = pattern; - this.statements = statements; - this.in_loc = in_loc; - this.then_loc = then_loc; + public InstanceVariableOperatorWriteNode(Location name_loc, Location operator_loc, Node value, byte[] operator, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + this.operator = operator; } public Node[] childNodes() { - return new Node[] { pattern, statements }; + return new Node[] { value }; } public T accept(AbstractNodeVisitor visitor) { - return visitor.visitInNode(this); + return visitor.visitInstanceVariableOperatorWriteNode(this); } } @@ -1394,8 +1937,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^ public static final class InstanceVariableReadNode extends Node { - public InstanceVariableReadNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public InstanceVariableReadNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -1416,8 +1959,8 @@ public static final class InstanceVariableWriteNode extends Node { public final Node value; // optional public final Location operator_loc; // optional - public InstanceVariableWriteNode(Location name_loc, Node value, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public InstanceVariableWriteNode(Location name_loc, Node value, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.name_loc = name_loc; this.value = value; this.operator_loc = operator_loc; @@ -1438,8 +1981,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^ public static final class IntegerNode extends Node { - public IntegerNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public IntegerNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -1456,15 +1999,17 @@ public T accept(AbstractNodeVisitor visitor) { // /foo #{bar} baz/ // ^^^^^^^^^^^^^^^^ public static final class InterpolatedRegularExpressionNode extends Node { - public final Token opening; + public final Location opening_loc; public final Node[] parts; - public final Token closing; + public final Location closing_loc; + public final int flags; - public InterpolatedRegularExpressionNode(Token opening, Node[] parts, Token closing, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.opening = opening; + public InterpolatedRegularExpressionNode(Location opening_loc, Node[] parts, Location closing_loc, int flags, int startOffset, int length) { + super(startOffset, length); + this.opening_loc = opening_loc; this.parts = parts; - this.closing = closing; + this.closing_loc = closing_loc; + this.flags = flags; } public Node[] childNodes() { @@ -1481,15 +2026,15 @@ public T accept(AbstractNodeVisitor visitor) { // "foo #{bar} baz" // ^^^^^^^^^^^^^^^^ public static final class InterpolatedStringNode extends Node { - public final Token opening; // optional + public final Location opening_loc; // optional public final Node[] parts; - public final Token closing; // optional + public final Location closing_loc; // optional - public InterpolatedStringNode(Token opening, Node[] parts, Token closing, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.opening = opening; + public InterpolatedStringNode(Location opening_loc, Node[] parts, Location closing_loc, int startOffset, int length) { + super(startOffset, length); + this.opening_loc = opening_loc; this.parts = parts; - this.closing = closing; + this.closing_loc = closing_loc; } public Node[] childNodes() { @@ -1506,15 +2051,15 @@ public T accept(AbstractNodeVisitor visitor) { // :"foo #{bar} baz" // ^^^^^^^^^^^^^^^^^ public static final class InterpolatedSymbolNode extends Node { - public final Token opening; // optional + public final Location opening_loc; // optional public final Node[] parts; - public final Token closing; // optional + public final Location closing_loc; // optional - public InterpolatedSymbolNode(Token opening, Node[] parts, Token closing, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.opening = opening; + public InterpolatedSymbolNode(Location opening_loc, Node[] parts, Location closing_loc, int startOffset, int length) { + super(startOffset, length); + this.opening_loc = opening_loc; this.parts = parts; - this.closing = closing; + this.closing_loc = closing_loc; } public Node[] childNodes() { @@ -1531,15 +2076,15 @@ public T accept(AbstractNodeVisitor visitor) { // `foo #{bar} baz` // ^^^^^^^^^^^^^^^^ public static final class InterpolatedXStringNode extends Node { - public final Token opening; + public final Location opening_loc; public final Node[] parts; - public final Token closing; + public final Location closing_loc; - public InterpolatedXStringNode(Token opening, Node[] parts, Token closing, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.opening = opening; + public InterpolatedXStringNode(Location opening_loc, Node[] parts, Location closing_loc, int startOffset, int length) { + super(startOffset, length); + this.opening_loc = opening_loc; this.parts = parts; - this.closing = closing; + this.closing_loc = closing_loc; } public Node[] childNodes() { @@ -1558,8 +2103,8 @@ public T accept(AbstractNodeVisitor visitor) { public static final class KeywordHashNode extends Node { public final Node[] elements; - public KeywordHashNode(Node[] elements, int startOffset, int endOffset) { - super(startOffset, endOffset); + public KeywordHashNode(Node[] elements, int startOffset, int length) { + super(startOffset, length); this.elements = elements; } @@ -1582,12 +2127,12 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^ // end public static final class KeywordParameterNode extends Node { - public final Token name; + public final Location name_loc; public final Node value; // optional - public KeywordParameterNode(Token name, Node value, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.name = name; + public KeywordParameterNode(Location name_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; this.value = value; } @@ -1606,13 +2151,13 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^ // end public static final class KeywordRestParameterNode extends Node { - public final Token operator; - public final Token name; // optional + public final Location operator_loc; + public final Location name_loc; // optional - public KeywordRestParameterNode(Token operator, Token name, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.operator = operator; - this.name = name; + public KeywordRestParameterNode(Location operator_loc, Location name_loc, int startOffset, int length) { + super(startOffset, length); + this.operator_loc = operator_loc; + this.name_loc = name_loc; } public Node[] childNodes() { @@ -1629,15 +2174,15 @@ public T accept(AbstractNodeVisitor visitor) { // ->(value) { value * 2 } // ^^^^^^^^^^^^^^^^^^^^^^^ public static final class LambdaNode extends Node { - public final Token[] locals; - public final Token opening; + public final byte[][] locals; + public final Location opening_loc; public final BlockParametersNode parameters; // optional public final Node statements; // optional - public LambdaNode(Token[] locals, Token opening, BlockParametersNode parameters, Node statements, int startOffset, int endOffset) { - super(startOffset, endOffset); + public LambdaNode(byte[][] locals, Location opening_loc, BlockParametersNode parameters, Node statements, int startOffset, int length) { + super(startOffset, length); this.locals = locals; - this.opening = opening; + this.opening_loc = opening_loc; this.parameters = parameters; this.statements = statements; } @@ -1651,6 +2196,89 @@ public T accept(AbstractNodeVisitor visitor) { } } + // Represents the use of the `&&=` operator for assignment to a local variable. + // + // target &&= value + // ^^^^^^^^^^^^^^^^ + public static final class LocalVariableOperatorAndWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + public final byte[] constant_id; + + public LocalVariableOperatorAndWriteNode(Location name_loc, Location operator_loc, Node value, byte[] constant_id, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + this.constant_id = constant_id; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitLocalVariableOperatorAndWriteNode(this); + } + } + + // Represents the use of the `||=` operator for assignment to a local variable. + // + // target ||= value + // ^^^^^^^^^^^^^^^^ + public static final class LocalVariableOperatorOrWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + public final byte[] constant_id; + + public LocalVariableOperatorOrWriteNode(Location name_loc, Location operator_loc, Node value, byte[] constant_id, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + this.constant_id = constant_id; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitLocalVariableOperatorOrWriteNode(this); + } + } + + // Represents assigning to a local variable using an operator that isn't `=`. + // + // target += value + // ^^^^^^^^^^^^^^^ + public static final class LocalVariableOperatorWriteNode extends Node { + public final Location name_loc; + public final Location operator_loc; + public final Node value; + public final byte[] constant_id; + public final byte[] operator_id; + + public LocalVariableOperatorWriteNode(Location name_loc, Location operator_loc, Node value, byte[] constant_id, byte[] operator_id, int startOffset, int length) { + super(startOffset, length); + this.name_loc = name_loc; + this.operator_loc = operator_loc; + this.value = value; + this.constant_id = constant_id; + this.operator_id = operator_id; + } + + public Node[] childNodes() { + return new Node[] { value }; + } + + public T accept(AbstractNodeVisitor visitor) { + return visitor.visitLocalVariableOperatorWriteNode(this); + } + } + // Represents reading a local variable. Note that this requires that a local // variable of the same name has already been written to in the same scope, // otherwise it is parsed as a method call. @@ -1658,10 +2286,12 @@ public T accept(AbstractNodeVisitor visitor) { // foo // ^^^ public static final class LocalVariableReadNode extends Node { + public final byte[] constant_id; public final int depth; - public LocalVariableReadNode(int depth, int startOffset, int endOffset) { - super(startOffset, endOffset); + public LocalVariableReadNode(byte[] constant_id, int depth, int startOffset, int length) { + super(startOffset, length); + this.constant_id = constant_id; this.depth = depth; } @@ -1679,17 +2309,19 @@ public T accept(AbstractNodeVisitor visitor) { // foo = 1 // ^^^^^^^ public static final class LocalVariableWriteNode extends Node { - public final Location name_loc; + public final byte[] constant_id; + public final int depth; public final Node value; // optional + public final Location name_loc; public final Location operator_loc; // optional - public final int depth; - public LocalVariableWriteNode(Location name_loc, Node value, Location operator_loc, int depth, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.name_loc = name_loc; + public LocalVariableWriteNode(byte[] constant_id, int depth, Node value, Location name_loc, Location operator_loc, int startOffset, int length) { + super(startOffset, length); + this.constant_id = constant_id; + this.depth = depth; this.value = value; + this.name_loc = name_loc; this.operator_loc = operator_loc; - this.depth = depth; } public Node[] childNodes() { @@ -1710,8 +2342,8 @@ public static final class MatchPredicateNode extends Node { public final Node pattern; public final Location operator_loc; - public MatchPredicateNode(Node value, Node pattern, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public MatchPredicateNode(Node value, Node pattern, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.value = value; this.pattern = pattern; this.operator_loc = operator_loc; @@ -1735,8 +2367,8 @@ public static final class MatchRequiredNode extends Node { public final Node pattern; public final Location operator_loc; - public MatchRequiredNode(Node value, Node pattern, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public MatchRequiredNode(Node value, Node pattern, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.value = value; this.pattern = pattern; this.operator_loc = operator_loc; @@ -1755,8 +2387,8 @@ public T accept(AbstractNodeVisitor visitor) { // error. public static final class MissingNode extends Node { - public MissingNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public MissingNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -1773,19 +2405,19 @@ public T accept(AbstractNodeVisitor visitor) { // module Foo end // ^^^^^^^^^^^^^^ public static final class ModuleNode extends Node { - public final Token[] locals; - public final Token module_keyword; + public final byte[][] locals; + public final Location module_keyword_loc; public final Node constant_path; public final Node statements; // optional - public final Token end_keyword; + public final Location end_keyword_loc; - public ModuleNode(Token[] locals, Token module_keyword, Node constant_path, Node statements, Token end_keyword, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ModuleNode(byte[][] locals, Location module_keyword_loc, Node constant_path, Node statements, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); this.locals = locals; - this.module_keyword = module_keyword; + this.module_keyword_loc = module_keyword_loc; this.constant_path = constant_path; this.statements = statements; - this.end_keyword = end_keyword; + this.end_keyword_loc = end_keyword_loc; } public Node[] childNodes() { @@ -1803,15 +2435,15 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^^^^^^^^^^^^^ public static final class MultiWriteNode extends Node { public final Node[] targets; - public final Token operator; // optional + public final Location operator_loc; // optional public final Node value; // optional public final Location lparen_loc; // optional public final Location rparen_loc; // optional - public MultiWriteNode(Node[] targets, Token operator, Node value, Location lparen_loc, Location rparen_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public MultiWriteNode(Node[] targets, Location operator_loc, Node value, Location lparen_loc, Location rparen_loc, int startOffset, int length) { + super(startOffset, length); this.targets = targets; - this.operator = operator; + this.operator_loc = operator_loc; this.value = value; this.lparen_loc = lparen_loc; this.rparen_loc = rparen_loc; @@ -1837,8 +2469,8 @@ public static final class NextNode extends Node { public final ArgumentsNode arguments; // optional public final Location keyword_loc; - public NextNode(ArgumentsNode arguments, Location keyword_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public NextNode(ArgumentsNode arguments, Location keyword_loc, int startOffset, int length) { + super(startOffset, length); this.arguments = arguments; this.keyword_loc = keyword_loc; } @@ -1858,8 +2490,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^ public static final class NilNode extends Node { - public NilNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public NilNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -1880,8 +2512,8 @@ public static final class NoKeywordsParameterNode extends Node { public final Location operator_loc; public final Location keyword_loc; - public NoKeywordsParameterNode(Location operator_loc, Location keyword_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public NoKeywordsParameterNode(Location operator_loc, Location keyword_loc, int startOffset, int length) { + super(startOffset, length); this.operator_loc = operator_loc; this.keyword_loc = keyword_loc; } @@ -1895,78 +2527,22 @@ public T accept(AbstractNodeVisitor visitor) { } } - // Represents the use of the `&&=` operator for assignment. - // - // target &&= value - // ^^^^^^^^^^^^^^^^ - public static final class OperatorAndAssignmentNode extends Node { - public final Node target; - public final Node value; - public final Location operator_loc; - - public OperatorAndAssignmentNode(Node target, Node value, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.target = target; - this.value = value; - this.operator_loc = operator_loc; - } - - public Node[] childNodes() { - return new Node[] { target, value }; - } - - public T accept(AbstractNodeVisitor visitor) { - return visitor.visitOperatorAndAssignmentNode(this); - } - } - - // Represents assigning to a value using an operator that isn't `=`. - // - // foo += bar - // ^^^^^^^^^^ - public static final class OperatorAssignmentNode extends Node { - public final Node target; - public final Token operator; - public final Node value; - - public OperatorAssignmentNode(Node target, Token operator, Node value, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.target = target; - this.operator = operator; - this.value = value; - } - - public Node[] childNodes() { - return new Node[] { target, value }; - } - - public T accept(AbstractNodeVisitor visitor) { - return visitor.visitOperatorAssignmentNode(this); - } - } - - // Represents the use of the `||=` operator for assignment. + // Represents reading a numbered reference to a capture in the previous match. // - // target ||= value - // ^^^^^^^^^^^^^^^^ - public static final class OperatorOrAssignmentNode extends Node { - public final Node target; - public final Node value; - public final Location operator_loc; + // $1 + // ^^ + public static final class NumberedReferenceReadNode extends Node { - public OperatorOrAssignmentNode(Node target, Node value, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.target = target; - this.value = value; - this.operator_loc = operator_loc; + public NumberedReferenceReadNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { - return new Node[] { target, value }; + return EMPTY_ARRAY; } public T accept(AbstractNodeVisitor visitor) { - return visitor.visitOperatorOrAssignmentNode(this); + return visitor.visitNumberedReferenceReadNode(this); } } @@ -1976,14 +2552,16 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^ // end public static final class OptionalParameterNode extends Node { - public final Token name; - public final Token equal_operator; + public final byte[] constant_id; + public final Location name_loc; + public final Location operator_loc; public final Node value; - public OptionalParameterNode(Token name, Token equal_operator, Node value, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.name = name; - this.equal_operator = equal_operator; + public OptionalParameterNode(byte[] constant_id, Location name_loc, Location operator_loc, Node value, int startOffset, int length) { + super(startOffset, length); + this.constant_id = constant_id; + this.name_loc = name_loc; + this.operator_loc = operator_loc; this.value = value; } @@ -2005,8 +2583,8 @@ public static final class OrNode extends Node { public final Node right; public final Location operator_loc; - public OrNode(Node left, Node right, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public OrNode(Node left, Node right, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.left = left; this.right = right; this.operator_loc = operator_loc; @@ -2035,8 +2613,8 @@ public static final class ParametersNode extends Node { public final Node keyword_rest; // optional public final BlockParameterNode block; // optional - public ParametersNode(Node[] requireds, Node[] optionals, Node[] posts, RestParameterNode rest, Node[] keywords, Node keyword_rest, BlockParameterNode block, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ParametersNode(Node[] requireds, Node[] optionals, Node[] posts, RestParameterNode rest, Node[] keywords, Node keyword_rest, BlockParameterNode block, int startOffset, int length) { + super(startOffset, length); this.requireds = requireds; this.optionals = optionals; this.posts = posts; @@ -2072,8 +2650,8 @@ public static final class ParenthesesNode extends Node { public final Location opening_loc; public final Location closing_loc; - public ParenthesesNode(Node statements, Location opening_loc, Location closing_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ParenthesesNode(Node statements, Location opening_loc, Location closing_loc, int startOffset, int length) { + super(startOffset, length); this.statements = statements; this.opening_loc = opening_loc; this.closing_loc = closing_loc; @@ -2099,8 +2677,8 @@ public static final class PinnedExpressionNode extends Node { public final Location lparen_loc; public final Location rparen_loc; - public PinnedExpressionNode(Node expression, Location operator_loc, Location lparen_loc, Location rparen_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public PinnedExpressionNode(Node expression, Location operator_loc, Location lparen_loc, Location rparen_loc, int startOffset, int length) { + super(startOffset, length); this.expression = expression; this.operator_loc = operator_loc; this.lparen_loc = lparen_loc; @@ -2125,8 +2703,8 @@ public static final class PinnedVariableNode extends Node { public final Node variable; public final Location operator_loc; - public PinnedVariableNode(Node variable, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public PinnedVariableNode(Node variable, Location operator_loc, int startOffset, int length) { + super(startOffset, length); this.variable = variable; this.operator_loc = operator_loc; } @@ -2145,13 +2723,13 @@ public T accept(AbstractNodeVisitor visitor) { // END { foo } // ^^^^^^^^^^^ public static final class PostExecutionNode extends Node { - public final StatementsNode statements; + public final StatementsNode statements; // optional public final Location keyword_loc; public final Location opening_loc; public final Location closing_loc; - public PostExecutionNode(StatementsNode statements, Location keyword_loc, Location opening_loc, Location closing_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public PostExecutionNode(StatementsNode statements, Location keyword_loc, Location opening_loc, Location closing_loc, int startOffset, int length) { + super(startOffset, length); this.statements = statements; this.keyword_loc = keyword_loc; this.opening_loc = opening_loc; @@ -2172,13 +2750,13 @@ public T accept(AbstractNodeVisitor visitor) { // BEGIN { foo } // ^^^^^^^^^^^^^ public static final class PreExecutionNode extends Node { - public final StatementsNode statements; + public final StatementsNode statements; // optional public final Location keyword_loc; public final Location opening_loc; public final Location closing_loc; - public PreExecutionNode(StatementsNode statements, Location keyword_loc, Location opening_loc, Location closing_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public PreExecutionNode(StatementsNode statements, Location keyword_loc, Location opening_loc, Location closing_loc, int startOffset, int length) { + super(startOffset, length); this.statements = statements; this.keyword_loc = keyword_loc; this.opening_loc = opening_loc; @@ -2196,11 +2774,11 @@ public T accept(AbstractNodeVisitor visitor) { // The top level node of any parse tree. public static final class ProgramNode extends Node { - public final Token[] locals; + public final byte[][] locals; public final StatementsNode statements; - public ProgramNode(Token[] locals, StatementsNode statements, int startOffset, int endOffset) { - super(startOffset, endOffset); + public ProgramNode(byte[][] locals, StatementsNode statements, int startOffset, int length) { + super(startOffset, length); this.locals = locals; this.statements = statements; } @@ -2225,12 +2803,14 @@ public static final class RangeNode extends Node { public final Node left; // optional public final Node right; // optional public final Location operator_loc; + public final int flags; - public RangeNode(Node left, Node right, Location operator_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public RangeNode(Node left, Node right, Location operator_loc, int flags, int startOffset, int length) { + super(startOffset, length); this.left = left; this.right = right; this.operator_loc = operator_loc; + this.flags = flags; } public Node[] childNodes() { @@ -2249,8 +2829,8 @@ public T accept(AbstractNodeVisitor visitor) { public static final class RationalNode extends Node { public final Node numeric; - public RationalNode(Node numeric, int startOffset, int endOffset) { - super(startOffset, endOffset); + public RationalNode(Node numeric, int startOffset, int length) { + super(startOffset, length); this.numeric = numeric; } @@ -2269,8 +2849,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^ public static final class RedoNode extends Node { - public RedoNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public RedoNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -2287,17 +2867,19 @@ public T accept(AbstractNodeVisitor visitor) { // /foo/i // ^^^^^^ public static final class RegularExpressionNode extends Node { - public final Token opening; - public final Token content; - public final Token closing; + public final Location opening_loc; + public final Location content_loc; + public final Location closing_loc; public final byte[] unescaped; + public final int flags; - public RegularExpressionNode(Token opening, Token content, Token closing, byte[] unescaped, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.opening = opening; - this.content = content; - this.closing = closing; + public RegularExpressionNode(Location opening_loc, Location content_loc, Location closing_loc, byte[] unescaped, int flags, int startOffset, int length) { + super(startOffset, length); + this.opening_loc = opening_loc; + this.content_loc = content_loc; + this.closing_loc = closing_loc; this.unescaped = unescaped; + this.flags = flags; } public Node[] childNodes() { @@ -2316,14 +2898,14 @@ public T accept(AbstractNodeVisitor visitor) { // end public static final class RequiredDestructuredParameterNode extends Node { public final Node[] parameters; - public final Token opening; - public final Token closing; + public final Location opening_loc; + public final Location closing_loc; - public RequiredDestructuredParameterNode(Node[] parameters, Token opening, Token closing, int startOffset, int endOffset) { - super(startOffset, endOffset); + public RequiredDestructuredParameterNode(Node[] parameters, Location opening_loc, Location closing_loc, int startOffset, int length) { + super(startOffset, length); this.parameters = parameters; - this.opening = opening; - this.closing = closing; + this.opening_loc = opening_loc; + this.closing_loc = closing_loc; } public Node[] childNodes() { @@ -2341,9 +2923,11 @@ public T accept(AbstractNodeVisitor visitor) { // ^ // end public static final class RequiredParameterNode extends Node { + public final byte[] constant_id; - public RequiredParameterNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public RequiredParameterNode(byte[] constant_id, int startOffset, int length) { + super(startOffset, length); + this.constant_id = constant_id; } public Node[] childNodes() { @@ -2361,13 +2945,13 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^^^^^^^^^^ public static final class RescueModifierNode extends Node { public final Node expression; - public final Token rescue_keyword; + public final Location keyword_loc; public final Node rescue_expression; - public RescueModifierNode(Node expression, Token rescue_keyword, Node rescue_expression, int startOffset, int endOffset) { - super(startOffset, endOffset); + public RescueModifierNode(Node expression, Location keyword_loc, Node rescue_expression, int startOffset, int length) { + super(startOffset, length); this.expression = expression; - this.rescue_keyword = rescue_keyword; + this.keyword_loc = keyword_loc; this.rescue_expression = rescue_expression; } @@ -2388,18 +2972,18 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^^ // end public static final class RescueNode extends Node { - public final Token rescue_keyword; + public final Location keyword_loc; public final Node[] exceptions; - public final Token equal_greater; // optional + public final Location operator_loc; // optional public final Node exception; // optional public final StatementsNode statements; // optional public final RescueNode consequent; // optional - public RescueNode(Token rescue_keyword, Node[] exceptions, Token equal_greater, Node exception, StatementsNode statements, RescueNode consequent, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.rescue_keyword = rescue_keyword; + public RescueNode(Location keyword_loc, Node[] exceptions, Location operator_loc, Node exception, StatementsNode statements, RescueNode consequent, int startOffset, int length) { + super(startOffset, length); + this.keyword_loc = keyword_loc; this.exceptions = exceptions; - this.equal_greater = equal_greater; + this.operator_loc = operator_loc; this.exception = exception; this.statements = statements; this.consequent = consequent; @@ -2425,13 +3009,13 @@ public T accept(AbstractNodeVisitor visitor) { // ^^ // end public static final class RestParameterNode extends Node { - public final Token operator; - public final Token name; // optional + public final Location operator_loc; + public final Location name_loc; // optional - public RestParameterNode(Token operator, Token name, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.operator = operator; - this.name = name; + public RestParameterNode(Location operator_loc, Location name_loc, int startOffset, int length) { + super(startOffset, length); + this.operator_loc = operator_loc; + this.name_loc = name_loc; } public Node[] childNodes() { @@ -2449,8 +3033,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^ public static final class RetryNode extends Node { - public RetryNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public RetryNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -2467,12 +3051,12 @@ public T accept(AbstractNodeVisitor visitor) { // return 1 // ^^^^^^^^ public static final class ReturnNode extends Node { - public final Token keyword; + public final Location keyword_loc; public final ArgumentsNode arguments; // optional - public ReturnNode(Token keyword, ArgumentsNode arguments, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.keyword = keyword; + public ReturnNode(Location keyword_loc, ArgumentsNode arguments, int startOffset, int length) { + super(startOffset, length); + this.keyword_loc = keyword_loc; this.arguments = arguments; } @@ -2491,8 +3075,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^ public static final class SelfNode extends Node { - public SelfNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public SelfNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -2509,21 +3093,21 @@ public T accept(AbstractNodeVisitor visitor) { // class << self end // ^^^^^^^^^^^^^^^^^ public static final class SingletonClassNode extends Node { - public final Token[] locals; - public final Token class_keyword; - public final Token operator; + public final byte[][] locals; + public final Location class_keyword_loc; + public final Location operator_loc; public final Node expression; public final Node statements; // optional - public final Token end_keyword; + public final Location end_keyword_loc; - public SingletonClassNode(Token[] locals, Token class_keyword, Token operator, Node expression, Node statements, Token end_keyword, int startOffset, int endOffset) { - super(startOffset, endOffset); + public SingletonClassNode(byte[][] locals, Location class_keyword_loc, Location operator_loc, Node expression, Node statements, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); this.locals = locals; - this.class_keyword = class_keyword; - this.operator = operator; + this.class_keyword_loc = class_keyword_loc; + this.operator_loc = operator_loc; this.expression = expression; this.statements = statements; - this.end_keyword = end_keyword; + this.end_keyword_loc = end_keyword_loc; } public Node[] childNodes() { @@ -2541,8 +3125,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^^^^^^^^ public static final class SourceEncodingNode extends Node { - public SourceEncodingNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public SourceEncodingNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -2561,8 +3145,8 @@ public T accept(AbstractNodeVisitor visitor) { public static final class SourceFileNode extends Node { public final byte[] filepath; - public SourceFileNode(byte[] filepath, int startOffset, int endOffset) { - super(startOffset, endOffset); + public SourceFileNode(byte[] filepath, int startOffset, int length) { + super(startOffset, length); this.filepath = filepath; } @@ -2581,8 +3165,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^^^^ public static final class SourceLineNode extends Node { - public SourceLineNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public SourceLineNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -2599,12 +3183,12 @@ public T accept(AbstractNodeVisitor visitor) { // [*a] // ^^ public static final class SplatNode extends Node { - public final Token operator; + public final Location operator_loc; public final Node expression; // optional - public SplatNode(Token operator, Node expression, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.operator = operator; + public SplatNode(Location operator_loc, Node expression, int startOffset, int length) { + super(startOffset, length); + this.operator_loc = operator_loc; this.expression = expression; } @@ -2624,8 +3208,8 @@ public T accept(AbstractNodeVisitor visitor) { public static final class StatementsNode extends Node { public final Node[] body; - public StatementsNode(Node[] body, int startOffset, int endOffset) { - super(startOffset, endOffset); + public StatementsNode(Node[] body, int startOffset, int length) { + super(startOffset, length); this.body = body; } @@ -2646,8 +3230,8 @@ public static final class StringConcatNode extends Node { public final Node left; public final Node right; - public StringConcatNode(Node left, Node right, int startOffset, int endOffset) { - super(startOffset, endOffset); + public StringConcatNode(Node left, Node right, int startOffset, int length) { + super(startOffset, length); this.left = left; this.right = right; } @@ -2661,31 +3245,6 @@ public T accept(AbstractNodeVisitor visitor) { } } - // Represents an interpolated set of statements within a string. - // - // "foo #{bar}" - // ^^^^^^ - public static final class StringInterpolatedNode extends Node { - public final Token opening; - public final StatementsNode statements; // optional - public final Token closing; - - public StringInterpolatedNode(Token opening, StatementsNode statements, Token closing, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.opening = opening; - this.statements = statements; - this.closing = closing; - } - - public Node[] childNodes() { - return new Node[] { statements }; - } - - public T accept(AbstractNodeVisitor visitor) { - return visitor.visitStringInterpolatedNode(this); - } - } - // Represents a string literal, a string contained within a `%w` list, or // plain string content within an interpolated string. // @@ -2698,16 +3257,16 @@ public T accept(AbstractNodeVisitor visitor) { // "foo #{bar} baz" // ^^^^ ^^^^ public static final class StringNode extends Node { - public final Token opening; // optional - public final Token content; - public final Token closing; // optional + public final Location opening_loc; // optional + public final Location content_loc; + public final Location closing_loc; // optional public final byte[] unescaped; - public StringNode(Token opening, Token content, Token closing, byte[] unescaped, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.opening = opening; - this.content = content; - this.closing = closing; + public StringNode(Location opening_loc, Location content_loc, Location closing_loc, byte[] unescaped, int startOffset, int length) { + super(startOffset, length); + this.opening_loc = opening_loc; + this.content_loc = content_loc; + this.closing_loc = closing_loc; this.unescaped = unescaped; } @@ -2728,18 +3287,18 @@ public T accept(AbstractNodeVisitor visitor) { // super foo, bar // ^^^^^^^^^^^^^^ public static final class SuperNode extends Node { - public final Token keyword; - public final Token lparen; // optional + public final Location keyword_loc; + public final Location lparen_loc; // optional public final ArgumentsNode arguments; // optional - public final Token rparen; // optional + public final Location rparen_loc; // optional public final BlockNode block; // optional - public SuperNode(Token keyword, Token lparen, ArgumentsNode arguments, Token rparen, BlockNode block, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.keyword = keyword; - this.lparen = lparen; + public SuperNode(Location keyword_loc, Location lparen_loc, ArgumentsNode arguments, Location rparen_loc, BlockNode block, int startOffset, int length) { + super(startOffset, length); + this.keyword_loc = keyword_loc; + this.lparen_loc = lparen_loc; this.arguments = arguments; - this.rparen = rparen; + this.rparen_loc = rparen_loc; this.block = block; } @@ -2760,16 +3319,16 @@ public T accept(AbstractNodeVisitor visitor) { // %i[foo] // ^^^ public static final class SymbolNode extends Node { - public final Token opening; // optional - public final Token value; - public final Token closing; // optional + public final Location opening_loc; // optional + public final Location value_loc; + public final Location closing_loc; // optional public final byte[] unescaped; - public SymbolNode(Token opening, Token value, Token closing, byte[] unescaped, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.opening = opening; - this.value = value; - this.closing = closing; + public SymbolNode(Location opening_loc, Location value_loc, Location closing_loc, byte[] unescaped, int startOffset, int length) { + super(startOffset, length); + this.opening_loc = opening_loc; + this.value_loc = value_loc; + this.closing_loc = closing_loc; this.unescaped = unescaped; } @@ -2788,8 +3347,8 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^ public static final class TrueNode extends Node { - public TrueNode(int startOffset, int endOffset) { - super(startOffset, endOffset); + public TrueNode(int startOffset, int length) { + super(startOffset, length); } public Node[] childNodes() { @@ -2809,8 +3368,8 @@ public static final class UndefNode extends Node { public final Node[] names; public final Location keyword_loc; - public UndefNode(Node[] names, Location keyword_loc, int startOffset, int endOffset) { - super(startOffset, endOffset); + public UndefNode(Node[] names, Location keyword_loc, int startOffset, int length) { + super(startOffset, length); this.names = names; this.keyword_loc = keyword_loc; } @@ -2832,19 +3391,19 @@ public T accept(AbstractNodeVisitor visitor) { // unless foo then bar end // ^^^^^^^^^^^^^^^^^^^^^^^ public static final class UnlessNode extends Node { - public final Token keyword; + public final Location keyword_loc; public final Node predicate; public final StatementsNode statements; // optional public final ElseNode consequent; // optional - public final Token end_keyword; // optional + public final Location end_keyword_loc; // optional - public UnlessNode(Token keyword, Node predicate, StatementsNode statements, ElseNode consequent, Token end_keyword, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.keyword = keyword; + public UnlessNode(Location keyword_loc, Node predicate, StatementsNode statements, ElseNode consequent, Location end_keyword_loc, int startOffset, int length) { + super(startOffset, length); + this.keyword_loc = keyword_loc; this.predicate = predicate; this.statements = statements; this.consequent = consequent; - this.end_keyword = end_keyword; + this.end_keyword_loc = end_keyword_loc; } public Node[] childNodes() { @@ -2864,13 +3423,13 @@ public T accept(AbstractNodeVisitor visitor) { // until foo do bar end // ^^^^^^^^^^^^^^^^^^^^ public static final class UntilNode extends Node { - public final Token keyword; + public final Location keyword_loc; public final Node predicate; public final StatementsNode statements; // optional - public UntilNode(Token keyword, Node predicate, StatementsNode statements, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.keyword = keyword; + public UntilNode(Location keyword_loc, Node predicate, StatementsNode statements, int startOffset, int length) { + super(startOffset, length); + this.keyword_loc = keyword_loc; this.predicate = predicate; this.statements = statements; } @@ -2889,13 +3448,13 @@ public T accept(AbstractNodeVisitor visitor) { // ^^^^^^^^^ // end public static final class WhenNode extends Node { - public final Token when_keyword; + public final Location keyword_loc; public final Node[] conditions; public final StatementsNode statements; // optional - public WhenNode(Token when_keyword, Node[] conditions, StatementsNode statements, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.when_keyword = when_keyword; + public WhenNode(Location keyword_loc, Node[] conditions, StatementsNode statements, int startOffset, int length) { + super(startOffset, length); + this.keyword_loc = keyword_loc; this.conditions = conditions; this.statements = statements; } @@ -2920,13 +3479,13 @@ public T accept(AbstractNodeVisitor visitor) { // while foo do bar end // ^^^^^^^^^^^^^^^^^^^^ public static final class WhileNode extends Node { - public final Token keyword; + public final Location keyword_loc; public final Node predicate; public final StatementsNode statements; // optional - public WhileNode(Token keyword, Node predicate, StatementsNode statements, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.keyword = keyword; + public WhileNode(Location keyword_loc, Node predicate, StatementsNode statements, int startOffset, int length) { + super(startOffset, length); + this.keyword_loc = keyword_loc; this.predicate = predicate; this.statements = statements; } @@ -2945,16 +3504,16 @@ public T accept(AbstractNodeVisitor visitor) { // `foo` // ^^^^^ public static final class XStringNode extends Node { - public final Token opening; - public final Token content; - public final Token closing; + public final Location opening_loc; + public final Location content_loc; + public final Location closing_loc; public final byte[] unescaped; - public XStringNode(Token opening, Token content, Token closing, byte[] unescaped, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.opening = opening; - this.content = content; - this.closing = closing; + public XStringNode(Location opening_loc, Location content_loc, Location closing_loc, byte[] unescaped, int startOffset, int length) { + super(startOffset, length); + this.opening_loc = opening_loc; + this.content_loc = content_loc; + this.closing_loc = closing_loc; this.unescaped = unescaped; } @@ -2972,17 +3531,17 @@ public T accept(AbstractNodeVisitor visitor) { // yield 1 // ^^^^^^^ public static final class YieldNode extends Node { - public final Token keyword; - public final Token lparen; // optional + public final Location keyword_loc; + public final Location lparen_loc; // optional public final ArgumentsNode arguments; // optional - public final Token rparen; // optional + public final Location rparen_loc; // optional - public YieldNode(Token keyword, Token lparen, ArgumentsNode arguments, Token rparen, int startOffset, int endOffset) { - super(startOffset, endOffset); - this.keyword = keyword; - this.lparen = lparen; + public YieldNode(Location keyword_loc, Location lparen_loc, ArgumentsNode arguments, Location rparen_loc, int startOffset, int length) { + super(startOffset, length); + this.keyword_loc = keyword_loc; + this.lparen_loc = lparen_loc; this.arguments = arguments; - this.rparen = rparen; + this.rparen_loc = rparen_loc; } public Node[] childNodes() { diff --git a/tool/import-yarp.sh b/tool/import-yarp.sh index f061e7ca4ec6..637b4fff4074 100755 --- a/tool/import-yarp.sh +++ b/tool/import-yarp.sh @@ -15,7 +15,7 @@ popd rm -rf src/main/c/yarp mkdir src/main/c/yarp cp -R $YARP/{include,src} src/main/c/yarp -cp $YARP/{configure.ac,LICENSE.md,Makefile.in} src/main/c/yarp +cp $YARP/{.gitignore,LICENSE.md,configure.ac,Makefile.in} src/main/c/yarp rm -rf src/yarp/java cp -R $YARP/java src/yarp/java From d84b6b2e5574234e60d91d26a8c111a75c0613fb Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 30 Jun 2023 17:06:07 +0200 Subject: [PATCH 04/10] Update graal to update mx --- ci/common.jsonnet | 11 +++++------ common.json | 16 ++++++++-------- mx.truffleruby/suite.py | 4 ++-- 3 files changed, 15 insertions(+), 16 deletions(-) diff --git a/ci/common.jsonnet b/ci/common.jsonnet index d48f4c2d8a9c..5c7834576655 100644 --- a/ci/common.jsonnet +++ b/ci/common.jsonnet @@ -42,17 +42,15 @@ local common_json = import "../common.json"; }, for name in std.objectFields(jdks_data) } + { - local latestJDKCE = self["labsjdk-ce-20"], - local latestJDKEE = self["labsjdk-ee-20"], # Some convenient JDK aliases which don't require ["name"] for frequently-used JDKs labsjdk17ce: self["labsjdk-ce-17"], labsjdk17ee: self["labsjdk-ee-17"], - labsjdk20ce: latestJDKCE, - labsjdk20ee: latestJDKEE, + labsjdk20ce: self["labsjdk-ce-20"], + labsjdk20ee: self["labsjdk-ee-20"], - labsjdkLatestCE: latestJDKCE, - labsjdkLatestEE: latestJDKEE, + labsjdkLatestCE: self["labsjdk-ce-21"], + labsjdkLatestEE: self["labsjdk-ee-21"], }, # The devkits versions reflect those used to build the JVMCI JDKs (e.g., see devkit_platform_revisions in /make/conf/jib-profiles.js) @@ -65,6 +63,7 @@ local common_json = import "../common.json"; "linux-jdk17": { packages+: { "devkit:gcc11.2.0-OL6.4+1": "==0" }}, "linux-jdk19": { packages+: { "devkit:gcc11.2.0-OL6.4+1": "==0" }}, "linux-jdk20": { packages+: { "devkit:gcc11.2.0-OL6.4+1": "==0" }}, + "linux-jdk21": { packages+: { "devkit:gcc11.2.0-OL6.4+1": "==0" }}, }, # Dependencies diff --git a/common.json b/common.json index 42103a6352a9..7f1d338a75e8 100644 --- a/common.json +++ b/common.json @@ -4,7 +4,7 @@ "Jsonnet files should not include this file directly but use ci/common.jsonnet instead." ], - "mx_version": "6.27.3", + "mx_version": "6.27.5", "COMMENT.jdks": "When adding or removing JDKs keep in sync with JDKs in ci/common.jsonnet", "jdks": { @@ -34,13 +34,13 @@ "labsjdk-ee-20Debug": {"name": "labsjdk", "version": "ee-20.0.2+2-jvmci-23.1-b02-debug", "platformspecific": true }, "labsjdk-ee-20-llvm": {"name": "labsjdk", "version": "ee-20.0.2+2-jvmci-23.1-b02-sulong", "platformspecific": true }, - "oraclejdk21": {"name": "jpg-jdk", "version": "21", "build_id": "27", "release": true, "platformspecific": true, "extrabundles": ["static-libs"]}, - "labsjdk-ce-21": {"name": "labsjdk", "version": "ce-21+27-jvmci-23.1-b08", "platformspecific": true }, - "labsjdk-ce-21Debug": {"name": "labsjdk", "version": "ce-21+27-jvmci-23.1-b08-debug", "platformspecific": true }, - "labsjdk-ce-21-llvm": {"name": "labsjdk", "version": "ce-21+27-jvmci-23.1-b08-sulong", "platformspecific": true }, - "labsjdk-ee-21": {"name": "labsjdk", "version": "ee-21+27-jvmci-23.1-b08", "platformspecific": true }, - "labsjdk-ee-21Debug": {"name": "labsjdk", "version": "ee-21+27-jvmci-23.1-b08-debug", "platformspecific": true }, - "labsjdk-ee-21-llvm": {"name": "labsjdk", "version": "ee-21+27-jvmci-23.1-b08-sulong", "platformspecific": true }, + "oraclejdk21": {"name": "jpg-jdk", "version": "21", "build_id": "28", "release": true, "platformspecific": true, "extrabundles": ["static-libs"]}, + "labsjdk-ce-21": {"name": "labsjdk", "version": "ce-21+28-jvmci-23.1-b09", "platformspecific": true }, + "labsjdk-ce-21Debug": {"name": "labsjdk", "version": "ce-21+28-jvmci-23.1-b09-debug", "platformspecific": true }, + "labsjdk-ce-21-llvm": {"name": "labsjdk", "version": "ce-21+28-jvmci-23.1-b09-sulong", "platformspecific": true }, + "labsjdk-ee-21": {"name": "labsjdk", "version": "ee-21+28-jvmci-23.1-b09", "platformspecific": true }, + "labsjdk-ee-21Debug": {"name": "labsjdk", "version": "ee-21+28-jvmci-23.1-b09-debug", "platformspecific": true }, + "labsjdk-ee-21-llvm": {"name": "labsjdk", "version": "ee-21+28-jvmci-23.1-b09-sulong", "platformspecific": true }, "oraclejdk22": {"name": "jpg-jdk", "version": "22", "build_id": "1", "release": true, "platformspecific": true, "extrabundles": ["static-libs"]} }, diff --git a/mx.truffleruby/suite.py b/mx.truffleruby/suite.py index 01db2099085d..6e0af3dd623d 100644 --- a/mx.truffleruby/suite.py +++ b/mx.truffleruby/suite.py @@ -7,7 +7,7 @@ { "name": "regex", "subdir": True, - "version": "f2f88bb722a6d10e714fa96351835a84f77d5e15", + "version": "f49685115ed0669641559593242832e06a437278", "urls": [ {"url": "https://github.com/oracle/graal.git", "kind": "git"}, {"url": "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind": "binary"}, @@ -16,7 +16,7 @@ { "name": "sulong", "subdir": True, - "version": "f2f88bb722a6d10e714fa96351835a84f77d5e15", + "version": "f49685115ed0669641559593242832e06a437278", "urls": [ {"url": "https://github.com/oracle/graal.git", "kind": "git"}, {"url": "https://curio.ssw.jku.at/nexus/content/repositories/snapshots", "kind": "binary"}, From e073b59a93cddbe09cdd92955222d956530b5584 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Fri, 30 Jun 2023 17:10:58 +0200 Subject: [PATCH 05/10] TruffleRuby needs mx 6.27.5+ to get the ldlibs substitution fix --- mx.truffleruby/suite.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mx.truffleruby/suite.py b/mx.truffleruby/suite.py index 6e0af3dd623d..2f83ccf7d19a 100644 --- a/mx.truffleruby/suite.py +++ b/mx.truffleruby/suite.py @@ -1,5 +1,5 @@ suite = { - "mxversion": "6.16.6", + "mxversion": "6.27.5", "name": "truffleruby", "imports": { From 32b97233affd0d2897295c12c9d795fadfc32583 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sun, 2 Jul 2023 13:51:07 +0200 Subject: [PATCH 06/10] Core files are not always available, they are only there if the truffleruby repository is around --- spec/truffle/yarp_spec.rb | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/spec/truffle/yarp_spec.rb b/spec/truffle/yarp_spec.rb index 686e9159d1f3..2a6ff94ecd03 100644 --- a/spec/truffle/yarp_spec.rb +++ b/spec/truffle/yarp_spec.rb @@ -12,10 +12,13 @@ # Similar tests as in https://github.com/ruby/yarp/blob/main/.github/workflows/truffleruby.yml describe "YARP" do - it "can parse core files" do - root = File.expand_path("../..", __dir__) - Dir.glob("#{root}/src/main/ruby/truffleruby/**/*.rb") do |file| - Truffle::Debug.yarp_parse(File.read(file)).should.include?("Node") + root = File.expand_path("../..", __dir__) + + guard -> { Dir.exist?("#{root}/src/main/ruby/truffleruby") } do + it "can parse core files" do + Dir.glob("#{root}/src/main/ruby/truffleruby/**/*.rb") do |file| + Truffle::Debug.yarp_parse(File.read(file)).should.include?("Node") + end end end From fa052338b96482c4e3fdde4325e4d7bbee429575 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Sun, 2 Jul 2023 13:53:44 +0200 Subject: [PATCH 07/10] autoreconf seems not available on macOS --- mx.truffleruby/mx_truffleruby.py | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/mx.truffleruby/mx_truffleruby.py b/mx.truffleruby/mx_truffleruby.py index 84399a86639d..a08a1f8563a7 100644 --- a/mx.truffleruby/mx_truffleruby.py +++ b/mx.truffleruby/mx_truffleruby.py @@ -135,7 +135,8 @@ def getBuildTask(self, args): class YARPNativeBuildTask(mx.NativeBuildTask): def build(self): - mx.run(['autoreconf'], cwd=self.subject.dir) + mx.run(['autoconf'], cwd=self.subject.dir) + mx.run(['autoheader'], cwd=self.subject.dir) mx.run(['./configure'], cwd=self.subject.dir) super(YARPNativeBuildTask, self).build() # make From 5dabddf9c365b3cf2817bbed2139d963f57b5380 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 3 Jul 2023 09:29:44 +0200 Subject: [PATCH 08/10] Import generated configure and Makefile from YARP to avoid dependency on autoconf --- mx.truffleruby/mx_truffleruby.py | 2 -- tool/import-yarp.sh | 3 ++- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/mx.truffleruby/mx_truffleruby.py b/mx.truffleruby/mx_truffleruby.py index a08a1f8563a7..37b4d2debdf8 100644 --- a/mx.truffleruby/mx_truffleruby.py +++ b/mx.truffleruby/mx_truffleruby.py @@ -135,8 +135,6 @@ def getBuildTask(self, args): class YARPNativeBuildTask(mx.NativeBuildTask): def build(self): - mx.run(['autoconf'], cwd=self.subject.dir) - mx.run(['autoheader'], cwd=self.subject.dir) mx.run(['./configure'], cwd=self.subject.dir) super(YARPNativeBuildTask, self).build() # make diff --git a/tool/import-yarp.sh b/tool/import-yarp.sh index 637b4fff4074..c386d9364630 100755 --- a/tool/import-yarp.sh +++ b/tool/import-yarp.sh @@ -10,12 +10,13 @@ pushd $YARP bundle bundle exec rake clobber bundle exec rake templates +bundle exec rake configure popd rm -rf src/main/c/yarp mkdir src/main/c/yarp cp -R $YARP/{include,src} src/main/c/yarp -cp $YARP/{.gitignore,LICENSE.md,configure.ac,Makefile.in} src/main/c/yarp +cp $YARP/{.gitignore,LICENSE.md,configure,config.h.in,Makefile.in} src/main/c/yarp rm -rf src/yarp/java cp -R $YARP/java src/yarp/java From b643fc1d6e099127a3a6a816af9a330c9faee5e8 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 3 Jul 2023 09:32:14 +0200 Subject: [PATCH 09/10] Import ruby/yarp@46bf785c6ed96ecde300cf9db857cff08f42467a --- src/main/c/yarp/.gitignore | 1 + src/main/c/yarp/Makefile.in | 27 + src/main/c/yarp/config.h.in | 43 + src/main/c/yarp/configure | 4509 ++++++++++++++++++++++++ src/main/c/yarp/configure.ac | 22 - src/main/c/yarp/include/yarp/defines.h | 5 + 6 files changed, 4585 insertions(+), 22 deletions(-) create mode 100644 src/main/c/yarp/config.h.in create mode 100755 src/main/c/yarp/configure delete mode 100644 src/main/c/yarp/configure.ac diff --git a/src/main/c/yarp/.gitignore b/src/main/c/yarp/.gitignore index 4dd8817511fd..3086b3da8326 100644 --- a/src/main/c/yarp/.gitignore +++ b/src/main/c/yarp/.gitignore @@ -22,6 +22,7 @@ test.c a.out /ext/yarp/api_node.c +/fuzz/output/ /include/yarp/ast.h /java/org/yarp/AbstractNodeVisitor.java /java/org/yarp/Loader.java diff --git a/src/main/c/yarp/Makefile.in b/src/main/c/yarp/Makefile.in index e44522bf441b..e3f4b68f53c3 100644 --- a/src/main/c/yarp/Makefile.in +++ b/src/main/c/yarp/Makefile.in @@ -6,6 +6,7 @@ Q1 = $(V:1=) Q = $(Q1:0=@) ECHO1 = $(V:1=@ :) ECHO = $(ECHO1:0=@ echo) +FUZZ_OUTPUT_DIR = $(shell pwd)/fuzz/output SOEXT := $(shell ruby -e 'puts RbConfig::CONFIG["SOEXT"]') @@ -42,6 +43,32 @@ build/static/%.o: src/%.c Makefile $(HEADERS) $(Q) mkdir -p $(@D) $(Q) $(CC) $(DEBUG_FLAGS) -DYP_STATIC $(CPPFLAGS) $(CFLAGS) -c -o $@ $< +build/fuzz.%: $(SOURCES) fuzz/%.c fuzz/fuzz.c + $(ECHO) "building $* fuzzer" + $(ECHO) "building main fuzz binary" + $(Q) AFL_HARDEN=1 afl-clang-lto $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) $(FUZZ_FLAGS) -O0 -fsanitize-ignorelist=fuzz/asan.ignore -fsanitize=fuzzer,address -ggdb3 -std=c99 -Iinclude -o $@ $^ + $(ECHO) "building cmplog binary" + $(Q) AFL_HARDEN=1 AFL_LLVM_CMPLOG=1 afl-clang-lto $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) $(FUZZ_FLAGS) -O0 -fsanitize-ignorelist=fuzz/asan.ignore -fsanitize=fuzzer,address -ggdb3 -std=c99 -Iinclude -o $@.cmplog $^ + +build/fuzz.heisenbug.%: $(SOURCES) fuzz/%.c fuzz/heisenbug.c + $(Q) AFL_HARDEN=1 afl-clang-lto $(DEBUG_FLAGS) $(CPPFLAGS) $(CFLAGS) $(FUZZ_FLAGS) -O0 -fsanitize-ignorelist=fuzz/asan.ignore -fsanitize=fuzzer,address -ggdb3 -std=c99 -Iinclude -o $@ $^ + +fuzz-debug: + $(ECHO) "entering debug shell" + $(Q) docker run -it --rm -e HISTFILE=/yarp/fuzz/output/.bash_history -v $(shell pwd):/yarp -v $(FUZZ_OUTPUT_DIR):/fuzz_output yarp/fuzz + +fuzz-docker-build: fuzz/docker/Dockerfile + $(ECHO) "building docker image" + $(Q) docker build -t yarp/fuzz fuzz/docker/ + +fuzz-run-%: FORCE fuzz-docker-build + $(ECHO) "running $* fuzzer" + $(Q) docker run --rm -v $(shell pwd):/yarp yarp/fuzz /bin/bash -c "FUZZ_FLAGS=\"$(FUZZ_FLAGS)\" make build/fuzz.$*" + $(ECHO) "starting AFL++ run" + $(Q) mkdir -p $(FUZZ_OUTPUT_DIR)/$* + $(Q) docker run -it --rm -v $(shell pwd):/yarp -v $(FUZZ_OUTPUT_DIR):/fuzz_output yarp/fuzz /bin/bash -c "./fuzz/$*.sh /fuzz_output/$*" +FORCE: + clean: $(Q) rm -f -r build diff --git a/src/main/c/yarp/config.h.in b/src/main/c/yarp/config.h.in new file mode 100644 index 000000000000..6f94a6975e86 --- /dev/null +++ b/src/main/c/yarp/config.h.in @@ -0,0 +1,43 @@ +/* config.h.in. Generated from configure.ac by autoheader. */ + +/* Define to 1 if you have the `mmap' function. */ +#undef HAVE_MMAP + +/* Define to 1 if you have the `snprintf' function. */ +#undef HAVE_SNPRINTF + +/* Define to 1 if you have the `strncasecmp' function. */ +#undef HAVE_STRNCASECMP + +/* Define to the address where bug reports for this package should be sent. */ +#undef PACKAGE_BUGREPORT + +/* Define to the full name of this package. */ +#undef PACKAGE_NAME + +/* Define to the full name and version of this package. */ +#undef PACKAGE_STRING + +/* Define to the one symbol short name of this package. */ +#undef PACKAGE_TARNAME + +/* Define to the home page for this package. */ +#undef PACKAGE_URL + +/* Define to the version of this package. */ +#undef PACKAGE_VERSION + +/* YP_VERSION */ +#undef YP_VERSION + +/* YP_VERSION_MAJOR */ +#undef YP_VERSION_MAJOR + +/* YP_VERSION_MINOR */ +#undef YP_VERSION_MINOR + +/* YP_VERSION_PATCH */ +#undef YP_VERSION_PATCH + +/* _XOPEN_SOURCE */ +#undef _XOPEN_SOURCE diff --git a/src/main/c/yarp/configure b/src/main/c/yarp/configure new file mode 100755 index 000000000000..ce275906cb46 --- /dev/null +++ b/src/main/c/yarp/configure @@ -0,0 +1,4509 @@ +#! /bin/sh +# Guess values for system-dependent variables and create Makefiles. +# Generated by GNU Autoconf 2.71 for YARP 0.4.0. +# +# Report bugs to . +# +# +# Copyright (C) 1992-1996, 1998-2017, 2020-2021 Free Software Foundation, +# Inc. +# +# +# This configure script is free software; the Free Software Foundation +# gives unlimited permission to copy, distribute and modify it. +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else $as_nop + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + + +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. +as_nl=' +' +export as_nl +IFS=" "" $as_nl" + +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi + +# The user is always right. +if ${PATH_SEPARATOR+false} :; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + + +# Use a proper internal environment variable to ensure we don't fall + # into an infinite loop, continuously re-executing ourselves. + if test x"${_as_can_reexec}" != xno && test "x$CONFIG_SHELL" != x; then + _as_can_reexec=no; export _as_can_reexec; + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 + fi + # We don't want this to propagate to other subprocesses. + { _as_can_reexec=; unset _as_can_reexec;} +if test "x$CONFIG_SHELL" = x; then + as_bourne_compatible="as_nop=: +if test \${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on \${1+\"\$@\"}, which + # is contrary to our usage. Disable this feature. + alias -g '\${1+\"\$@\"}'='\"\$@\"' + setopt NO_GLOB_SUBST +else \$as_nop + case \`(set -o) 2>/dev/null\` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi +" + as_required="as_fn_return () { (exit \$1); } +as_fn_success () { as_fn_return 0; } +as_fn_failure () { as_fn_return 1; } +as_fn_ret_success () { return 0; } +as_fn_ret_failure () { return 1; } + +exitcode=0 +as_fn_success || { exitcode=1; echo as_fn_success failed.; } +as_fn_failure && { exitcode=1; echo as_fn_failure succeeded.; } +as_fn_ret_success || { exitcode=1; echo as_fn_ret_success failed.; } +as_fn_ret_failure && { exitcode=1; echo as_fn_ret_failure succeeded.; } +if ( set x; as_fn_ret_success y && test x = \"\$1\" ) +then : + +else \$as_nop + exitcode=1; echo positional parameters were not saved. +fi +test x\$exitcode = x0 || exit 1 +blah=\$(echo \$(echo blah)) +test x\"\$blah\" = xblah || exit 1 +test -x / || exit 1" + as_suggested=" as_lineno_1=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_1a=\$LINENO + as_lineno_2=";as_suggested=$as_suggested$LINENO;as_suggested=$as_suggested" as_lineno_2a=\$LINENO + eval 'test \"x\$as_lineno_1'\$as_run'\" != \"x\$as_lineno_2'\$as_run'\" && + test \"x\`expr \$as_lineno_1'\$as_run' + 1\`\" = \"x\$as_lineno_2'\$as_run'\"' || exit 1" + if (eval "$as_required") 2>/dev/null +then : + as_have_required=yes +else $as_nop + as_have_required=no +fi + if test x$as_have_required = xyes && (eval "$as_suggested") 2>/dev/null +then : + +else $as_nop + as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +as_found=false +for as_dir in /bin$PATH_SEPARATOR/usr/bin$PATH_SEPARATOR$PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + as_found=: + case $as_dir in #( + /*) + for as_base in sh bash ksh sh5; do + # Try only shells that exist, to save several forks. + as_shell=$as_dir$as_base + if { test -f "$as_shell" || test -f "$as_shell.exe"; } && + as_run=a "$as_shell" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : + CONFIG_SHELL=$as_shell as_have_required=yes + if as_run=a "$as_shell" -c "$as_bourne_compatible""$as_suggested" 2>/dev/null +then : + break 2 +fi +fi + done;; + esac + as_found=false +done +IFS=$as_save_IFS +if $as_found +then : + +else $as_nop + if { test -f "$SHELL" || test -f "$SHELL.exe"; } && + as_run=a "$SHELL" -c "$as_bourne_compatible""$as_required" 2>/dev/null +then : + CONFIG_SHELL=$SHELL as_have_required=yes +fi +fi + + + if test "x$CONFIG_SHELL" != x +then : + export CONFIG_SHELL + # We cannot yet assume a decent shell, so we have to provide a +# neutralization value for shells without unset; and this also +# works around shells that cannot unset nonexistent variables. +# Preserve -v and -x to the replacement shell. +BASH_ENV=/dev/null +ENV=/dev/null +(unset BASH_ENV) >/dev/null 2>&1 && unset BASH_ENV ENV +case $- in # (((( + *v*x* | *x*v* ) as_opts=-vx ;; + *v* ) as_opts=-v ;; + *x* ) as_opts=-x ;; + * ) as_opts= ;; +esac +exec $CONFIG_SHELL $as_opts "$as_myself" ${1+"$@"} +# Admittedly, this is quite paranoid, since all the known shells bail +# out after a failed `exec'. +printf "%s\n" "$0: could not re-execute with $CONFIG_SHELL" >&2 +exit 255 +fi + + if test x$as_have_required = xno +then : + printf "%s\n" "$0: This script requires a shell more modern than all" + printf "%s\n" "$0: the shells that I found on your system." + if test ${ZSH_VERSION+y} ; then + printf "%s\n" "$0: In particular, zsh $ZSH_VERSION has bugs and should" + printf "%s\n" "$0: be upgraded to zsh 4.3.4 or later." + else + printf "%s\n" "$0: Please tell bug-autoconf@gnu.org and +$0: https://github.com/ruby/yarp/issues/new about your +$0: system, including any error possibly output before this +$0: message. Then install a modern shell, or manually run +$0: the script under such a shell if you do have one." + fi + exit 1 +fi +fi +fi +SHELL=${CONFIG_SHELL-/bin/sh} +export SHELL +# Unset more variables known to interfere with behavior of common tools. +CLICOLOR_FORCE= GREP_OPTIONS= +unset CLICOLOR_FORCE GREP_OPTIONS + +## --------------------- ## +## M4sh Shell Functions. ## +## --------------------- ## +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else $as_nop + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else $as_nop + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + +# as_fn_nop +# --------- +# Do nothing but, unlike ":", preserve the value of $?. +as_fn_nop () +{ + return $? +} +as_nop=as_fn_nop + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + printf "%s\n" "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + + as_lineno_1=$LINENO as_lineno_1a=$LINENO + as_lineno_2=$LINENO as_lineno_2a=$LINENO + eval 'test "x$as_lineno_1'$as_run'" != "x$as_lineno_2'$as_run'" && + test "x`expr $as_lineno_1'$as_run' + 1`" = "x$as_lineno_2'$as_run'"' || { + # Blame Lee E. McMahon (1931-1989) for sed's syntax. :-) + sed -n ' + p + /[$]LINENO/= + ' <$as_myself | + sed ' + s/[$]LINENO.*/&-/ + t lineno + b + :lineno + N + :loop + s/[$]LINENO\([^'$as_cr_alnum'_].*\n\)\(.*\)/\2\1\2/ + t loop + s/-\n.*// + ' >$as_me.lineno && + chmod +x "$as_me.lineno" || + { printf "%s\n" "$as_me: error: cannot create $as_me.lineno; rerun with a POSIX shell" >&2; as_fn_exit 1; } + + # If we had to re-execute with $CONFIG_SHELL, we're ensured to have + # already done that, so ensure we don't try to do so again and fall + # in an infinite loop. This has already happened in practice. + _as_can_reexec=no; export _as_can_reexec + # Don't try to exec as it changes $[0], causing all sort of problems + # (the dirname of $[0] is not the place where we might find the + # original and so on. Autoconf is especially sensitive to this). + . "./$as_me.lineno" + # Exit status is that of the last command. + exit +} + + +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' + + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +test -n "$DJDIR" || exec 7<&0 &1 + +# Name of the host. +# hostname on some systems (SVR3.2, old GNU/Linux) returns a bogus exit status, +# so uname gets run too. +ac_hostname=`(hostname || uname -n) 2>/dev/null | sed 1q` + +# +# Initializations. +# +ac_default_prefix=/usr/local +ac_clean_files= +ac_config_libobj_dir=. +LIBOBJS= +cross_compiling=no +subdirs= +MFLAGS= +MAKEFLAGS= + +# Identity of this package. +PACKAGE_NAME='YARP' +PACKAGE_TARNAME='yarp' +PACKAGE_VERSION='0.4.0' +PACKAGE_STRING='YARP 0.4.0' +PACKAGE_BUGREPORT='https://github.com/ruby/yarp/issues/new' +PACKAGE_URL='https://github.com/ruby/yarp' + +ac_subst_vars='LTLIBOBJS +LIBOBJS +OBJEXT +EXEEXT +ac_ct_CC +CPPFLAGS +LDFLAGS +CFLAGS +CC +target_alias +host_alias +build_alias +LIBS +ECHO_T +ECHO_N +ECHO_C +DEFS +mandir +localedir +libdir +psdir +pdfdir +dvidir +htmldir +infodir +docdir +oldincludedir +includedir +runstatedir +localstatedir +sharedstatedir +sysconfdir +datadir +datarootdir +libexecdir +sbindir +bindir +program_transform_name +prefix +exec_prefix +PACKAGE_URL +PACKAGE_BUGREPORT +PACKAGE_STRING +PACKAGE_VERSION +PACKAGE_TARNAME +PACKAGE_NAME +PATH_SEPARATOR +SHELL' +ac_subst_files='' +ac_user_opts=' +enable_option_checking +' + ac_precious_vars='build_alias +host_alias +target_alias +CC +CFLAGS +LDFLAGS +LIBS +CPPFLAGS' + + +# Initialize some variables set by options. +ac_init_help= +ac_init_version=false +ac_unrecognized_opts= +ac_unrecognized_sep= +# The variables have the same names as the options, with +# dashes changed to underlines. +cache_file=/dev/null +exec_prefix=NONE +no_create= +no_recursion= +prefix=NONE +program_prefix=NONE +program_suffix=NONE +program_transform_name=s,x,x, +silent= +site= +srcdir= +verbose= +x_includes=NONE +x_libraries=NONE + +# Installation directory options. +# These are left unexpanded so users can "make install exec_prefix=/foo" +# and all the variables that are supposed to be based on exec_prefix +# by default will actually change. +# Use braces instead of parens because sh, perl, etc. also accept them. +# (The list follows the same order as the GNU Coding Standards.) +bindir='${exec_prefix}/bin' +sbindir='${exec_prefix}/sbin' +libexecdir='${exec_prefix}/libexec' +datarootdir='${prefix}/share' +datadir='${datarootdir}' +sysconfdir='${prefix}/etc' +sharedstatedir='${prefix}/com' +localstatedir='${prefix}/var' +runstatedir='${localstatedir}/run' +includedir='${prefix}/include' +oldincludedir='/usr/include' +docdir='${datarootdir}/doc/${PACKAGE_TARNAME}' +infodir='${datarootdir}/info' +htmldir='${docdir}' +dvidir='${docdir}' +pdfdir='${docdir}' +psdir='${docdir}' +libdir='${exec_prefix}/lib' +localedir='${datarootdir}/locale' +mandir='${datarootdir}/man' + +ac_prev= +ac_dashdash= +for ac_option +do + # If the previous option needs an argument, assign it. + if test -n "$ac_prev"; then + eval $ac_prev=\$ac_option + ac_prev= + continue + fi + + case $ac_option in + *=?*) ac_optarg=`expr "X$ac_option" : '[^=]*=\(.*\)'` ;; + *=) ac_optarg= ;; + *) ac_optarg=yes ;; + esac + + case $ac_dashdash$ac_option in + --) + ac_dashdash=yes ;; + + -bindir | --bindir | --bindi | --bind | --bin | --bi) + ac_prev=bindir ;; + -bindir=* | --bindir=* | --bindi=* | --bind=* | --bin=* | --bi=*) + bindir=$ac_optarg ;; + + -build | --build | --buil | --bui | --bu) + ac_prev=build_alias ;; + -build=* | --build=* | --buil=* | --bui=* | --bu=*) + build_alias=$ac_optarg ;; + + -cache-file | --cache-file | --cache-fil | --cache-fi \ + | --cache-f | --cache- | --cache | --cach | --cac | --ca | --c) + ac_prev=cache_file ;; + -cache-file=* | --cache-file=* | --cache-fil=* | --cache-fi=* \ + | --cache-f=* | --cache-=* | --cache=* | --cach=* | --cac=* | --ca=* | --c=*) + cache_file=$ac_optarg ;; + + --config-cache | -C) + cache_file=config.cache ;; + + -datadir | --datadir | --datadi | --datad) + ac_prev=datadir ;; + -datadir=* | --datadir=* | --datadi=* | --datad=*) + datadir=$ac_optarg ;; + + -datarootdir | --datarootdir | --datarootdi | --datarootd | --dataroot \ + | --dataroo | --dataro | --datar) + ac_prev=datarootdir ;; + -datarootdir=* | --datarootdir=* | --datarootdi=* | --datarootd=* \ + | --dataroot=* | --dataroo=* | --dataro=* | --datar=*) + datarootdir=$ac_optarg ;; + + -disable-* | --disable-*) + ac_useropt=`expr "x$ac_option" : 'x-*disable-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--disable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=no ;; + + -docdir | --docdir | --docdi | --doc | --do) + ac_prev=docdir ;; + -docdir=* | --docdir=* | --docdi=* | --doc=* | --do=*) + docdir=$ac_optarg ;; + + -dvidir | --dvidir | --dvidi | --dvid | --dvi | --dv) + ac_prev=dvidir ;; + -dvidir=* | --dvidir=* | --dvidi=* | --dvid=* | --dvi=* | --dv=*) + dvidir=$ac_optarg ;; + + -enable-* | --enable-*) + ac_useropt=`expr "x$ac_option" : 'x-*enable-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid feature name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"enable_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--enable-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval enable_$ac_useropt=\$ac_optarg ;; + + -exec-prefix | --exec_prefix | --exec-prefix | --exec-prefi \ + | --exec-pref | --exec-pre | --exec-pr | --exec-p | --exec- \ + | --exec | --exe | --ex) + ac_prev=exec_prefix ;; + -exec-prefix=* | --exec_prefix=* | --exec-prefix=* | --exec-prefi=* \ + | --exec-pref=* | --exec-pre=* | --exec-pr=* | --exec-p=* | --exec-=* \ + | --exec=* | --exe=* | --ex=*) + exec_prefix=$ac_optarg ;; + + -gas | --gas | --ga | --g) + # Obsolete; use --with-gas. + with_gas=yes ;; + + -help | --help | --hel | --he | -h) + ac_init_help=long ;; + -help=r* | --help=r* | --hel=r* | --he=r* | -hr*) + ac_init_help=recursive ;; + -help=s* | --help=s* | --hel=s* | --he=s* | -hs*) + ac_init_help=short ;; + + -host | --host | --hos | --ho) + ac_prev=host_alias ;; + -host=* | --host=* | --hos=* | --ho=*) + host_alias=$ac_optarg ;; + + -htmldir | --htmldir | --htmldi | --htmld | --html | --htm | --ht) + ac_prev=htmldir ;; + -htmldir=* | --htmldir=* | --htmldi=* | --htmld=* | --html=* | --htm=* \ + | --ht=*) + htmldir=$ac_optarg ;; + + -includedir | --includedir | --includedi | --included | --include \ + | --includ | --inclu | --incl | --inc) + ac_prev=includedir ;; + -includedir=* | --includedir=* | --includedi=* | --included=* | --include=* \ + | --includ=* | --inclu=* | --incl=* | --inc=*) + includedir=$ac_optarg ;; + + -infodir | --infodir | --infodi | --infod | --info | --inf) + ac_prev=infodir ;; + -infodir=* | --infodir=* | --infodi=* | --infod=* | --info=* | --inf=*) + infodir=$ac_optarg ;; + + -libdir | --libdir | --libdi | --libd) + ac_prev=libdir ;; + -libdir=* | --libdir=* | --libdi=* | --libd=*) + libdir=$ac_optarg ;; + + -libexecdir | --libexecdir | --libexecdi | --libexecd | --libexec \ + | --libexe | --libex | --libe) + ac_prev=libexecdir ;; + -libexecdir=* | --libexecdir=* | --libexecdi=* | --libexecd=* | --libexec=* \ + | --libexe=* | --libex=* | --libe=*) + libexecdir=$ac_optarg ;; + + -localedir | --localedir | --localedi | --localed | --locale) + ac_prev=localedir ;; + -localedir=* | --localedir=* | --localedi=* | --localed=* | --locale=*) + localedir=$ac_optarg ;; + + -localstatedir | --localstatedir | --localstatedi | --localstated \ + | --localstate | --localstat | --localsta | --localst | --locals) + ac_prev=localstatedir ;; + -localstatedir=* | --localstatedir=* | --localstatedi=* | --localstated=* \ + | --localstate=* | --localstat=* | --localsta=* | --localst=* | --locals=*) + localstatedir=$ac_optarg ;; + + -mandir | --mandir | --mandi | --mand | --man | --ma | --m) + ac_prev=mandir ;; + -mandir=* | --mandir=* | --mandi=* | --mand=* | --man=* | --ma=* | --m=*) + mandir=$ac_optarg ;; + + -nfp | --nfp | --nf) + # Obsolete; use --without-fp. + with_fp=no ;; + + -no-create | --no-create | --no-creat | --no-crea | --no-cre \ + | --no-cr | --no-c | -n) + no_create=yes ;; + + -no-recursion | --no-recursion | --no-recursio | --no-recursi \ + | --no-recurs | --no-recur | --no-recu | --no-rec | --no-re | --no-r) + no_recursion=yes ;; + + -oldincludedir | --oldincludedir | --oldincludedi | --oldincluded \ + | --oldinclude | --oldinclud | --oldinclu | --oldincl | --oldinc \ + | --oldin | --oldi | --old | --ol | --o) + ac_prev=oldincludedir ;; + -oldincludedir=* | --oldincludedir=* | --oldincludedi=* | --oldincluded=* \ + | --oldinclude=* | --oldinclud=* | --oldinclu=* | --oldincl=* | --oldinc=* \ + | --oldin=* | --oldi=* | --old=* | --ol=* | --o=*) + oldincludedir=$ac_optarg ;; + + -prefix | --prefix | --prefi | --pref | --pre | --pr | --p) + ac_prev=prefix ;; + -prefix=* | --prefix=* | --prefi=* | --pref=* | --pre=* | --pr=* | --p=*) + prefix=$ac_optarg ;; + + -program-prefix | --program-prefix | --program-prefi | --program-pref \ + | --program-pre | --program-pr | --program-p) + ac_prev=program_prefix ;; + -program-prefix=* | --program-prefix=* | --program-prefi=* \ + | --program-pref=* | --program-pre=* | --program-pr=* | --program-p=*) + program_prefix=$ac_optarg ;; + + -program-suffix | --program-suffix | --program-suffi | --program-suff \ + | --program-suf | --program-su | --program-s) + ac_prev=program_suffix ;; + -program-suffix=* | --program-suffix=* | --program-suffi=* \ + | --program-suff=* | --program-suf=* | --program-su=* | --program-s=*) + program_suffix=$ac_optarg ;; + + -program-transform-name | --program-transform-name \ + | --program-transform-nam | --program-transform-na \ + | --program-transform-n | --program-transform- \ + | --program-transform | --program-transfor \ + | --program-transfo | --program-transf \ + | --program-trans | --program-tran \ + | --progr-tra | --program-tr | --program-t) + ac_prev=program_transform_name ;; + -program-transform-name=* | --program-transform-name=* \ + | --program-transform-nam=* | --program-transform-na=* \ + | --program-transform-n=* | --program-transform-=* \ + | --program-transform=* | --program-transfor=* \ + | --program-transfo=* | --program-transf=* \ + | --program-trans=* | --program-tran=* \ + | --progr-tra=* | --program-tr=* | --program-t=*) + program_transform_name=$ac_optarg ;; + + -pdfdir | --pdfdir | --pdfdi | --pdfd | --pdf | --pd) + ac_prev=pdfdir ;; + -pdfdir=* | --pdfdir=* | --pdfdi=* | --pdfd=* | --pdf=* | --pd=*) + pdfdir=$ac_optarg ;; + + -psdir | --psdir | --psdi | --psd | --ps) + ac_prev=psdir ;; + -psdir=* | --psdir=* | --psdi=* | --psd=* | --ps=*) + psdir=$ac_optarg ;; + + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + silent=yes ;; + + -runstatedir | --runstatedir | --runstatedi | --runstated \ + | --runstate | --runstat | --runsta | --runst | --runs \ + | --run | --ru | --r) + ac_prev=runstatedir ;; + -runstatedir=* | --runstatedir=* | --runstatedi=* | --runstated=* \ + | --runstate=* | --runstat=* | --runsta=* | --runst=* | --runs=* \ + | --run=* | --ru=* | --r=*) + runstatedir=$ac_optarg ;; + + -sbindir | --sbindir | --sbindi | --sbind | --sbin | --sbi | --sb) + ac_prev=sbindir ;; + -sbindir=* | --sbindir=* | --sbindi=* | --sbind=* | --sbin=* \ + | --sbi=* | --sb=*) + sbindir=$ac_optarg ;; + + -sharedstatedir | --sharedstatedir | --sharedstatedi \ + | --sharedstated | --sharedstate | --sharedstat | --sharedsta \ + | --sharedst | --shareds | --shared | --share | --shar \ + | --sha | --sh) + ac_prev=sharedstatedir ;; + -sharedstatedir=* | --sharedstatedir=* | --sharedstatedi=* \ + | --sharedstated=* | --sharedstate=* | --sharedstat=* | --sharedsta=* \ + | --sharedst=* | --shareds=* | --shared=* | --share=* | --shar=* \ + | --sha=* | --sh=*) + sharedstatedir=$ac_optarg ;; + + -site | --site | --sit) + ac_prev=site ;; + -site=* | --site=* | --sit=*) + site=$ac_optarg ;; + + -srcdir | --srcdir | --srcdi | --srcd | --src | --sr) + ac_prev=srcdir ;; + -srcdir=* | --srcdir=* | --srcdi=* | --srcd=* | --src=* | --sr=*) + srcdir=$ac_optarg ;; + + -sysconfdir | --sysconfdir | --sysconfdi | --sysconfd | --sysconf \ + | --syscon | --sysco | --sysc | --sys | --sy) + ac_prev=sysconfdir ;; + -sysconfdir=* | --sysconfdir=* | --sysconfdi=* | --sysconfd=* | --sysconf=* \ + | --syscon=* | --sysco=* | --sysc=* | --sys=* | --sy=*) + sysconfdir=$ac_optarg ;; + + -target | --target | --targe | --targ | --tar | --ta | --t) + ac_prev=target_alias ;; + -target=* | --target=* | --targe=* | --targ=* | --tar=* | --ta=* | --t=*) + target_alias=$ac_optarg ;; + + -v | -verbose | --verbose | --verbos | --verbo | --verb) + verbose=yes ;; + + -version | --version | --versio | --versi | --vers | -V) + ac_init_version=: ;; + + -with-* | --with-*) + ac_useropt=`expr "x$ac_option" : 'x-*with-\([^=]*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--with-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=\$ac_optarg ;; + + -without-* | --without-*) + ac_useropt=`expr "x$ac_option" : 'x-*without-\(.*\)'` + # Reject names that are not valid shell variable names. + expr "x$ac_useropt" : ".*[^-+._$as_cr_alnum]" >/dev/null && + as_fn_error $? "invalid package name: \`$ac_useropt'" + ac_useropt_orig=$ac_useropt + ac_useropt=`printf "%s\n" "$ac_useropt" | sed 's/[-+.]/_/g'` + case $ac_user_opts in + *" +"with_$ac_useropt" +"*) ;; + *) ac_unrecognized_opts="$ac_unrecognized_opts$ac_unrecognized_sep--without-$ac_useropt_orig" + ac_unrecognized_sep=', ';; + esac + eval with_$ac_useropt=no ;; + + --x) + # Obsolete; use --with-x. + with_x=yes ;; + + -x-includes | --x-includes | --x-include | --x-includ | --x-inclu \ + | --x-incl | --x-inc | --x-in | --x-i) + ac_prev=x_includes ;; + -x-includes=* | --x-includes=* | --x-include=* | --x-includ=* | --x-inclu=* \ + | --x-incl=* | --x-inc=* | --x-in=* | --x-i=*) + x_includes=$ac_optarg ;; + + -x-libraries | --x-libraries | --x-librarie | --x-librari \ + | --x-librar | --x-libra | --x-libr | --x-lib | --x-li | --x-l) + ac_prev=x_libraries ;; + -x-libraries=* | --x-libraries=* | --x-librarie=* | --x-librari=* \ + | --x-librar=* | --x-libra=* | --x-libr=* | --x-lib=* | --x-li=* | --x-l=*) + x_libraries=$ac_optarg ;; + + -*) as_fn_error $? "unrecognized option: \`$ac_option' +Try \`$0 --help' for more information" + ;; + + *=*) + ac_envvar=`expr "x$ac_option" : 'x\([^=]*\)='` + # Reject names that are not valid shell variable names. + case $ac_envvar in #( + '' | [0-9]* | *[!_$as_cr_alnum]* ) + as_fn_error $? "invalid variable name: \`$ac_envvar'" ;; + esac + eval $ac_envvar=\$ac_optarg + export $ac_envvar ;; + + *) + # FIXME: should be removed in autoconf 3.0. + printf "%s\n" "$as_me: WARNING: you should use --build, --host, --target" >&2 + expr "x$ac_option" : ".*[^-._$as_cr_alnum]" >/dev/null && + printf "%s\n" "$as_me: WARNING: invalid host type: $ac_option" >&2 + : "${build_alias=$ac_option} ${host_alias=$ac_option} ${target_alias=$ac_option}" + ;; + + esac +done + +if test -n "$ac_prev"; then + ac_option=--`echo $ac_prev | sed 's/_/-/g'` + as_fn_error $? "missing argument to $ac_option" +fi + +if test -n "$ac_unrecognized_opts"; then + case $enable_option_checking in + no) ;; + fatal) as_fn_error $? "unrecognized options: $ac_unrecognized_opts" ;; + *) printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2 ;; + esac +fi + +# Check all directory arguments for consistency. +for ac_var in exec_prefix prefix bindir sbindir libexecdir datarootdir \ + datadir sysconfdir sharedstatedir localstatedir includedir \ + oldincludedir docdir infodir htmldir dvidir pdfdir psdir \ + libdir localedir mandir runstatedir +do + eval ac_val=\$$ac_var + # Remove trailing slashes. + case $ac_val in + */ ) + ac_val=`expr "X$ac_val" : 'X\(.*[^/]\)' \| "X$ac_val" : 'X\(.*\)'` + eval $ac_var=\$ac_val;; + esac + # Be sure to have absolute directory names. + case $ac_val in + [\\/$]* | ?:[\\/]* ) continue;; + NONE | '' ) case $ac_var in *prefix ) continue;; esac;; + esac + as_fn_error $? "expected an absolute directory name for --$ac_var: $ac_val" +done + +# There might be people who depend on the old broken behavior: `$host' +# used to hold the argument of --host etc. +# FIXME: To remove some day. +build=$build_alias +host=$host_alias +target=$target_alias + +# FIXME: To remove some day. +if test "x$host_alias" != x; then + if test "x$build_alias" = x; then + cross_compiling=maybe + elif test "x$build_alias" != "x$host_alias"; then + cross_compiling=yes + fi +fi + +ac_tool_prefix= +test -n "$host_alias" && ac_tool_prefix=$host_alias- + +test "$silent" = yes && exec 6>/dev/null + + +ac_pwd=`pwd` && test -n "$ac_pwd" && +ac_ls_di=`ls -di .` && +ac_pwd_ls_di=`cd "$ac_pwd" && ls -di .` || + as_fn_error $? "working directory cannot be determined" +test "X$ac_ls_di" = "X$ac_pwd_ls_di" || + as_fn_error $? "pwd does not report name of working directory" + + +# Find the source files, if location was not specified. +if test -z "$srcdir"; then + ac_srcdir_defaulted=yes + # Try the directory containing this script, then the parent directory. + ac_confdir=`$as_dirname -- "$as_myself" || +$as_expr X"$as_myself" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_myself" : 'X\(//\)[^/]' \| \ + X"$as_myself" : 'X\(//\)$' \| \ + X"$as_myself" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_myself" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + srcdir=$ac_confdir + if test ! -r "$srcdir/$ac_unique_file"; then + srcdir=.. + fi +else + ac_srcdir_defaulted=no +fi +if test ! -r "$srcdir/$ac_unique_file"; then + test "$ac_srcdir_defaulted" = yes && srcdir="$ac_confdir or .." + as_fn_error $? "cannot find sources ($ac_unique_file) in $srcdir" +fi +ac_msg="sources are in $srcdir, but \`cd $srcdir' does not work" +ac_abs_confdir=`( + cd "$srcdir" && test -r "./$ac_unique_file" || as_fn_error $? "$ac_msg" + pwd)` +# When building in place, set srcdir=. +if test "$ac_abs_confdir" = "$ac_pwd"; then + srcdir=. +fi +# Remove unnecessary trailing slashes from srcdir. +# Double slashes in file names in object file debugging info +# mess up M-x gdb in Emacs. +case $srcdir in +*/) srcdir=`expr "X$srcdir" : 'X\(.*[^/]\)' \| "X$srcdir" : 'X\(.*\)'`;; +esac +for ac_var in $ac_precious_vars; do + eval ac_env_${ac_var}_set=\${${ac_var}+set} + eval ac_env_${ac_var}_value=\$${ac_var} + eval ac_cv_env_${ac_var}_set=\${${ac_var}+set} + eval ac_cv_env_${ac_var}_value=\$${ac_var} +done + +# +# Report the --help message. +# +if test "$ac_init_help" = "long"; then + # Omit some internal or obsolete options to make the list less imposing. + # This message is too long to be a string in the A/UX 3.1 sh. + cat <<_ACEOF +\`configure' configures YARP 0.4.0 to adapt to many kinds of systems. + +Usage: $0 [OPTION]... [VAR=VALUE]... + +To assign environment variables (e.g., CC, CFLAGS...), specify them as +VAR=VALUE. See below for descriptions of some of the useful variables. + +Defaults for the options are specified in brackets. + +Configuration: + -h, --help display this help and exit + --help=short display options specific to this package + --help=recursive display the short help of all the included packages + -V, --version display version information and exit + -q, --quiet, --silent do not print \`checking ...' messages + --cache-file=FILE cache test results in FILE [disabled] + -C, --config-cache alias for \`--cache-file=config.cache' + -n, --no-create do not create output files + --srcdir=DIR find the sources in DIR [configure dir or \`..'] + +Installation directories: + --prefix=PREFIX install architecture-independent files in PREFIX + [$ac_default_prefix] + --exec-prefix=EPREFIX install architecture-dependent files in EPREFIX + [PREFIX] + +By default, \`make install' will install all the files in +\`$ac_default_prefix/bin', \`$ac_default_prefix/lib' etc. You can specify +an installation prefix other than \`$ac_default_prefix' using \`--prefix', +for instance \`--prefix=\$HOME'. + +For better control, use the options below. + +Fine tuning of the installation directories: + --bindir=DIR user executables [EPREFIX/bin] + --sbindir=DIR system admin executables [EPREFIX/sbin] + --libexecdir=DIR program executables [EPREFIX/libexec] + --sysconfdir=DIR read-only single-machine data [PREFIX/etc] + --sharedstatedir=DIR modifiable architecture-independent data [PREFIX/com] + --localstatedir=DIR modifiable single-machine data [PREFIX/var] + --runstatedir=DIR modifiable per-process data [LOCALSTATEDIR/run] + --libdir=DIR object code libraries [EPREFIX/lib] + --includedir=DIR C header files [PREFIX/include] + --oldincludedir=DIR C header files for non-gcc [/usr/include] + --datarootdir=DIR read-only arch.-independent data root [PREFIX/share] + --datadir=DIR read-only architecture-independent data [DATAROOTDIR] + --infodir=DIR info documentation [DATAROOTDIR/info] + --localedir=DIR locale-dependent data [DATAROOTDIR/locale] + --mandir=DIR man documentation [DATAROOTDIR/man] + --docdir=DIR documentation root [DATAROOTDIR/doc/yarp] + --htmldir=DIR html documentation [DOCDIR] + --dvidir=DIR dvi documentation [DOCDIR] + --pdfdir=DIR pdf documentation [DOCDIR] + --psdir=DIR ps documentation [DOCDIR] +_ACEOF + + cat <<\_ACEOF +_ACEOF +fi + +if test -n "$ac_init_help"; then + case $ac_init_help in + short | recursive ) echo "Configuration of YARP 0.4.0:";; + esac + cat <<\_ACEOF + +Some influential environment variables: + CC C compiler command + CFLAGS C compiler flags + LDFLAGS linker flags, e.g. -L if you have libraries in a + nonstandard directory + LIBS libraries to pass to the linker, e.g. -l + CPPFLAGS (Objective) C/C++ preprocessor flags, e.g. -I if + you have headers in a nonstandard directory + +Use these variables to override the choices made by `configure' or to help +it to find libraries and programs with nonstandard names/locations. + +Report bugs to . +YARP home page: . +_ACEOF +ac_status=$? +fi + +if test "$ac_init_help" = "recursive"; then + # If there are subdirs, report their specific --help. + for ac_dir in : $ac_subdirs_all; do test "x$ac_dir" = x: && continue + test -d "$ac_dir" || + { cd "$srcdir" && ac_pwd=`pwd` && srcdir=. && test -d "$ac_dir"; } || + continue + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + cd "$ac_dir" || { ac_status=$?; continue; } + # Check for configure.gnu first; this name is used for a wrapper for + # Metaconfig's "Configure" on case-insensitive file systems. + if test -f "$ac_srcdir/configure.gnu"; then + echo && + $SHELL "$ac_srcdir/configure.gnu" --help=recursive + elif test -f "$ac_srcdir/configure"; then + echo && + $SHELL "$ac_srcdir/configure" --help=recursive + else + printf "%s\n" "$as_me: WARNING: no configuration information is in $ac_dir" >&2 + fi || ac_status=$? + cd "$ac_pwd" || { ac_status=$?; break; } + done +fi + +test -n "$ac_init_help" && exit $ac_status +if $ac_init_version; then + cat <<\_ACEOF +YARP configure 0.4.0 +generated by GNU Autoconf 2.71 + +Copyright (C) 2021 Free Software Foundation, Inc. +This configure script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it. +_ACEOF + exit +fi + +## ------------------------ ## +## Autoconf initialization. ## +## ------------------------ ## + +# ac_fn_c_try_compile LINENO +# -------------------------- +# Try to compile conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_compile () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest.beam + if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest.$ac_objext +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_compile + +# ac_fn_c_try_link LINENO +# ----------------------- +# Try to link conftest.$ac_ext, and return whether this succeeded. +ac_fn_c_try_link () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + rm -f conftest.$ac_objext conftest.beam conftest$ac_exeext + if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + grep -v '^ *+' conftest.err >conftest.er1 + cat conftest.er1 >&5 + mv -f conftest.er1 conftest.err + fi + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } && { + test -z "$ac_c_werror_flag" || + test ! -s conftest.err + } && test -s conftest$ac_exeext && { + test "$cross_compiling" = yes || + test -x conftest$ac_exeext + } +then : + ac_retval=0 +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + + ac_retval=1 +fi + # Delete the IPA/IPO (Inter Procedural Analysis/Optimization) information + # created by the PGI compiler (conftest_ipa8_conftest.oo), as it would + # interfere with the next link command; also delete a directory that is + # left behind by Apple's compiler. We do this before executing the actions. + rm -rf conftest.dSYM conftest_ipa8_conftest.oo + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + as_fn_set_status $ac_retval + +} # ac_fn_c_try_link + +# ac_fn_c_check_func LINENO FUNC VAR +# ---------------------------------- +# Tests whether FUNC exists, setting the cache variable VAR accordingly +ac_fn_c_check_func () +{ + as_lineno=${as_lineno-"$1"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $2" >&5 +printf %s "checking for $2... " >&6; } +if eval test \${$3+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +/* Define $2 to an innocuous variant, in case declares $2. + For example, HP-UX 11i declares gettimeofday. */ +#define $2 innocuous_$2 + +/* System header to define __stub macros and hopefully few prototypes, + which can conflict with char $2 (); below. */ + +#include +#undef $2 + +/* Override any GCC internal prototype to avoid an error. + Use char because int might match the return type of a GCC + builtin and then its argument prototype would still apply. */ +#ifdef __cplusplus +extern "C" +#endif +char $2 (); +/* The GNU C library defines this for functions which it implements + to always fail with ENOSYS. Some functions are actually named + something starting with __ and the normal name is an alias. */ +#if defined __stub_$2 || defined __stub___$2 +choke me +#endif + +int +main (void) +{ +return $2 (); + ; + return 0; +} +_ACEOF +if ac_fn_c_try_link "$LINENO" +then : + eval "$3=yes" +else $as_nop + eval "$3=no" +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam \ + conftest$ac_exeext conftest.$ac_ext +fi +eval ac_res=\$$3 + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_res" >&5 +printf "%s\n" "$ac_res" >&6; } + eval $as_lineno_stack; ${as_lineno_stack:+:} unset as_lineno + +} # ac_fn_c_check_func +ac_configure_args_raw= +for ac_arg +do + case $ac_arg in + *\'*) + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append ac_configure_args_raw " '$ac_arg'" +done + +case $ac_configure_args_raw in + *$as_nl*) + ac_safe_unquote= ;; + *) + ac_unsafe_z='|&;<>()$`\\"*?[ '' ' # This string ends in space, tab. + ac_unsafe_a="$ac_unsafe_z#~" + ac_safe_unquote="s/ '\\([^$ac_unsafe_a][^$ac_unsafe_z]*\\)'/ \\1/g" + ac_configure_args_raw=` printf "%s\n" "$ac_configure_args_raw" | sed "$ac_safe_unquote"`;; +esac + +cat >config.log <<_ACEOF +This file contains any messages produced by compilers while +running configure, to aid debugging if configure makes a mistake. + +It was created by YARP $as_me 0.4.0, which was +generated by GNU Autoconf 2.71. Invocation command line was + + $ $0$ac_configure_args_raw + +_ACEOF +exec 5>>config.log +{ +cat <<_ASUNAME +## --------- ## +## Platform. ## +## --------- ## + +hostname = `(hostname || uname -n) 2>/dev/null | sed 1q` +uname -m = `(uname -m) 2>/dev/null || echo unknown` +uname -r = `(uname -r) 2>/dev/null || echo unknown` +uname -s = `(uname -s) 2>/dev/null || echo unknown` +uname -v = `(uname -v) 2>/dev/null || echo unknown` + +/usr/bin/uname -p = `(/usr/bin/uname -p) 2>/dev/null || echo unknown` +/bin/uname -X = `(/bin/uname -X) 2>/dev/null || echo unknown` + +/bin/arch = `(/bin/arch) 2>/dev/null || echo unknown` +/usr/bin/arch -k = `(/usr/bin/arch -k) 2>/dev/null || echo unknown` +/usr/convex/getsysinfo = `(/usr/convex/getsysinfo) 2>/dev/null || echo unknown` +/usr/bin/hostinfo = `(/usr/bin/hostinfo) 2>/dev/null || echo unknown` +/bin/machine = `(/bin/machine) 2>/dev/null || echo unknown` +/usr/bin/oslevel = `(/usr/bin/oslevel) 2>/dev/null || echo unknown` +/bin/universe = `(/bin/universe) 2>/dev/null || echo unknown` + +_ASUNAME + +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + printf "%s\n" "PATH: $as_dir" + done +IFS=$as_save_IFS + +} >&5 + +cat >&5 <<_ACEOF + + +## ----------- ## +## Core tests. ## +## ----------- ## + +_ACEOF + + +# Keep a trace of the command line. +# Strip out --no-create and --no-recursion so they do not pile up. +# Strip out --silent because we don't want to record it for future runs. +# Also quote any args containing shell meta-characters. +# Make two passes to allow for proper duplicate-argument suppression. +ac_configure_args= +ac_configure_args0= +ac_configure_args1= +ac_must_keep_next=false +for ac_pass in 1 2 +do + for ac_arg + do + case $ac_arg in + -no-create | --no-c* | -n | -no-recursion | --no-r*) continue ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil) + continue ;; + *\'*) + ac_arg=`printf "%s\n" "$ac_arg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + case $ac_pass in + 1) as_fn_append ac_configure_args0 " '$ac_arg'" ;; + 2) + as_fn_append ac_configure_args1 " '$ac_arg'" + if test $ac_must_keep_next = true; then + ac_must_keep_next=false # Got value, back to normal. + else + case $ac_arg in + *=* | --config-cache | -C | -disable-* | --disable-* \ + | -enable-* | --enable-* | -gas | --g* | -nfp | --nf* \ + | -q | -quiet | --q* | -silent | --sil* | -v | -verb* \ + | -with-* | --with-* | -without-* | --without-* | --x) + case "$ac_configure_args0 " in + "$ac_configure_args1"*" '$ac_arg' "* ) continue ;; + esac + ;; + -* ) ac_must_keep_next=true ;; + esac + fi + as_fn_append ac_configure_args " '$ac_arg'" + ;; + esac + done +done +{ ac_configure_args0=; unset ac_configure_args0;} +{ ac_configure_args1=; unset ac_configure_args1;} + +# When interrupted or exit'd, cleanup temporary files, and complete +# config.log. We remove comments because anyway the quotes in there +# would cause problems or look ugly. +# WARNING: Use '\'' to represent an apostrophe within the trap. +# WARNING: Do not start the trap code with a newline, due to a FreeBSD 4.0 bug. +trap 'exit_status=$? + # Sanitize IFS. + IFS=" "" $as_nl" + # Save into config.log some information that might help in debugging. + { + echo + + printf "%s\n" "## ---------------- ## +## Cache variables. ## +## ---------------- ##" + echo + # The following way of writing the cache mishandles newlines in values, +( + for ac_var in `(set) 2>&1 | sed -n '\''s/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'\''`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + (set) 2>&1 | + case $as_nl`(ac_space='\'' '\''; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + sed -n \ + "s/'\''/'\''\\\\'\'''\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\''\\2'\''/p" + ;; #( + *) + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) + echo + + printf "%s\n" "## ----------------- ## +## Output variables. ## +## ----------------- ##" + echo + for ac_var in $ac_subst_vars + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + printf "%s\n" "$ac_var='\''$ac_val'\''" + done | sort + echo + + if test -n "$ac_subst_files"; then + printf "%s\n" "## ------------------- ## +## File substitutions. ## +## ------------------- ##" + echo + for ac_var in $ac_subst_files + do + eval ac_val=\$$ac_var + case $ac_val in + *\'\''*) ac_val=`printf "%s\n" "$ac_val" | sed "s/'\''/'\''\\\\\\\\'\'''\''/g"`;; + esac + printf "%s\n" "$ac_var='\''$ac_val'\''" + done | sort + echo + fi + + if test -s confdefs.h; then + printf "%s\n" "## ----------- ## +## confdefs.h. ## +## ----------- ##" + echo + cat confdefs.h + echo + fi + test "$ac_signal" != 0 && + printf "%s\n" "$as_me: caught signal $ac_signal" + printf "%s\n" "$as_me: exit $exit_status" + } >&5 + rm -f core *.core core.conftest.* && + rm -f -r conftest* confdefs* conf$$* $ac_clean_files && + exit $exit_status +' 0 +for ac_signal in 1 2 13 15; do + trap 'ac_signal='$ac_signal'; as_fn_exit 1' $ac_signal +done +ac_signal=0 + +# confdefs.h avoids OS command line length limits that DEFS can exceed. +rm -f -r conftest* confdefs.h + +printf "%s\n" "/* confdefs.h */" > confdefs.h + +# Predefined preprocessor variables. + +printf "%s\n" "#define PACKAGE_NAME \"$PACKAGE_NAME\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_TARNAME \"$PACKAGE_TARNAME\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_VERSION \"$PACKAGE_VERSION\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_STRING \"$PACKAGE_STRING\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_BUGREPORT \"$PACKAGE_BUGREPORT\"" >>confdefs.h + +printf "%s\n" "#define PACKAGE_URL \"$PACKAGE_URL\"" >>confdefs.h + + +# Let the site file select an alternate cache file if it wants to. +# Prefer an explicitly selected file to automatically selected ones. +if test -n "$CONFIG_SITE"; then + ac_site_files="$CONFIG_SITE" +elif test "x$prefix" != xNONE; then + ac_site_files="$prefix/share/config.site $prefix/etc/config.site" +else + ac_site_files="$ac_default_prefix/share/config.site $ac_default_prefix/etc/config.site" +fi + +for ac_site_file in $ac_site_files +do + case $ac_site_file in #( + */*) : + ;; #( + *) : + ac_site_file=./$ac_site_file ;; +esac + if test -f "$ac_site_file" && test -r "$ac_site_file"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading site script $ac_site_file" >&5 +printf "%s\n" "$as_me: loading site script $ac_site_file" >&6;} + sed 's/^/| /' "$ac_site_file" >&5 + . "$ac_site_file" \ + || { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "failed to load site script $ac_site_file +See \`config.log' for more details" "$LINENO" 5; } + fi +done + +if test -r "$cache_file"; then + # Some versions of bash will fail to source /dev/null (special files + # actually), so we avoid doing that. DJGPP emulates it as a regular file. + if test /dev/null != "$cache_file" && test -f "$cache_file"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: loading cache $cache_file" >&5 +printf "%s\n" "$as_me: loading cache $cache_file" >&6;} + case $cache_file in + [\\/]* | ?:[\\/]* ) . "$cache_file";; + *) . "./$cache_file";; + esac + fi +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating cache $cache_file" >&5 +printf "%s\n" "$as_me: creating cache $cache_file" >&6;} + >$cache_file +fi + +# Test code for whether the C compiler supports C89 (global declarations) +ac_c_conftest_c89_globals=' +/* Does the compiler advertise C89 conformance? + Do not test the value of __STDC__, because some compilers set it to 0 + while being otherwise adequately conformant. */ +#if !defined __STDC__ +# error "Compiler does not advertise C89 conformance" +#endif + +#include +#include +struct stat; +/* Most of the following tests are stolen from RCS 5.7 src/conf.sh. */ +struct buf { int x; }; +struct buf * (*rcsopen) (struct buf *, struct stat *, int); +static char *e (p, i) + char **p; + int i; +{ + return p[i]; +} +static char *f (char * (*g) (char **, int), char **p, ...) +{ + char *s; + va_list v; + va_start (v,p); + s = g (p, va_arg (v,int)); + va_end (v); + return s; +} + +/* OSF 4.0 Compaq cc is some sort of almost-ANSI by default. It has + function prototypes and stuff, but not \xHH hex character constants. + These do not provoke an error unfortunately, instead are silently treated + as an "x". The following induces an error, until -std is added to get + proper ANSI mode. Curiously \x00 != x always comes out true, for an + array size at least. It is necessary to write \x00 == 0 to get something + that is true only with -std. */ +int osf4_cc_array ['\''\x00'\'' == 0 ? 1 : -1]; + +/* IBM C 6 for AIX is almost-ANSI by default, but it replaces macro parameters + inside strings and character constants. */ +#define FOO(x) '\''x'\'' +int xlc6_cc_array[FOO(a) == '\''x'\'' ? 1 : -1]; + +int test (int i, double x); +struct s1 {int (*f) (int a);}; +struct s2 {int (*f) (double a);}; +int pairnames (int, char **, int *(*)(struct buf *, struct stat *, int), + int, int);' + +# Test code for whether the C compiler supports C89 (body of main). +ac_c_conftest_c89_main=' +ok |= (argc == 0 || f (e, argv, 0) != argv[0] || f (e, argv, 1) != argv[1]); +' + +# Test code for whether the C compiler supports C99 (global declarations) +ac_c_conftest_c99_globals=' +// Does the compiler advertise C99 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 199901L +# error "Compiler does not advertise C99 conformance" +#endif + +#include +extern int puts (const char *); +extern int printf (const char *, ...); +extern int dprintf (int, const char *, ...); +extern void *malloc (size_t); + +// Check varargs macros. These examples are taken from C99 6.10.3.5. +// dprintf is used instead of fprintf to avoid needing to declare +// FILE and stderr. +#define debug(...) dprintf (2, __VA_ARGS__) +#define showlist(...) puts (#__VA_ARGS__) +#define report(test,...) ((test) ? puts (#test) : printf (__VA_ARGS__)) +static void +test_varargs_macros (void) +{ + int x = 1234; + int y = 5678; + debug ("Flag"); + debug ("X = %d\n", x); + showlist (The first, second, and third items.); + report (x>y, "x is %d but y is %d", x, y); +} + +// Check long long types. +#define BIG64 18446744073709551615ull +#define BIG32 4294967295ul +#define BIG_OK (BIG64 / BIG32 == 4294967297ull && BIG64 % BIG32 == 0) +#if !BIG_OK + #error "your preprocessor is broken" +#endif +#if BIG_OK +#else + #error "your preprocessor is broken" +#endif +static long long int bignum = -9223372036854775807LL; +static unsigned long long int ubignum = BIG64; + +struct incomplete_array +{ + int datasize; + double data[]; +}; + +struct named_init { + int number; + const wchar_t *name; + double average; +}; + +typedef const char *ccp; + +static inline int +test_restrict (ccp restrict text) +{ + // See if C++-style comments work. + // Iterate through items via the restricted pointer. + // Also check for declarations in for loops. + for (unsigned int i = 0; *(text+i) != '\''\0'\''; ++i) + continue; + return 0; +} + +// Check varargs and va_copy. +static bool +test_varargs (const char *format, ...) +{ + va_list args; + va_start (args, format); + va_list args_copy; + va_copy (args_copy, args); + + const char *str = ""; + int number = 0; + float fnumber = 0; + + while (*format) + { + switch (*format++) + { + case '\''s'\'': // string + str = va_arg (args_copy, const char *); + break; + case '\''d'\'': // int + number = va_arg (args_copy, int); + break; + case '\''f'\'': // float + fnumber = va_arg (args_copy, double); + break; + default: + break; + } + } + va_end (args_copy); + va_end (args); + + return *str && number && fnumber; +} +' + +# Test code for whether the C compiler supports C99 (body of main). +ac_c_conftest_c99_main=' + // Check bool. + _Bool success = false; + success |= (argc != 0); + + // Check restrict. + if (test_restrict ("String literal") == 0) + success = true; + char *restrict newvar = "Another string"; + + // Check varargs. + success &= test_varargs ("s, d'\'' f .", "string", 65, 34.234); + test_varargs_macros (); + + // Check flexible array members. + struct incomplete_array *ia = + malloc (sizeof (struct incomplete_array) + (sizeof (double) * 10)); + ia->datasize = 10; + for (int i = 0; i < ia->datasize; ++i) + ia->data[i] = i * 1.234; + + // Check named initializers. + struct named_init ni = { + .number = 34, + .name = L"Test wide string", + .average = 543.34343, + }; + + ni.number = 58; + + int dynamic_array[ni.number]; + dynamic_array[0] = argv[0][0]; + dynamic_array[ni.number - 1] = 543; + + // work around unused variable warnings + ok |= (!success || bignum == 0LL || ubignum == 0uLL || newvar[0] == '\''x'\'' + || dynamic_array[ni.number - 1] != 543); +' + +# Test code for whether the C compiler supports C11 (global declarations) +ac_c_conftest_c11_globals=' +// Does the compiler advertise C11 conformance? +#if !defined __STDC_VERSION__ || __STDC_VERSION__ < 201112L +# error "Compiler does not advertise C11 conformance" +#endif + +// Check _Alignas. +char _Alignas (double) aligned_as_double; +char _Alignas (0) no_special_alignment; +extern char aligned_as_int; +char _Alignas (0) _Alignas (int) aligned_as_int; + +// Check _Alignof. +enum +{ + int_alignment = _Alignof (int), + int_array_alignment = _Alignof (int[100]), + char_alignment = _Alignof (char) +}; +_Static_assert (0 < -_Alignof (int), "_Alignof is signed"); + +// Check _Noreturn. +int _Noreturn does_not_return (void) { for (;;) continue; } + +// Check _Static_assert. +struct test_static_assert +{ + int x; + _Static_assert (sizeof (int) <= sizeof (long int), + "_Static_assert does not work in struct"); + long int y; +}; + +// Check UTF-8 literals. +#define u8 syntax error! +char const utf8_literal[] = u8"happens to be ASCII" "another string"; + +// Check duplicate typedefs. +typedef long *long_ptr; +typedef long int *long_ptr; +typedef long_ptr long_ptr; + +// Anonymous structures and unions -- taken from C11 6.7.2.1 Example 1. +struct anonymous +{ + union { + struct { int i; int j; }; + struct { int k; long int l; } w; + }; + int m; +} v1; +' + +# Test code for whether the C compiler supports C11 (body of main). +ac_c_conftest_c11_main=' + _Static_assert ((offsetof (struct anonymous, i) + == offsetof (struct anonymous, w.k)), + "Anonymous union alignment botch"); + v1.i = 2; + v1.w.k = 5; + ok |= v1.i != 5; +' + +# Test code for whether the C compiler supports C11 (complete). +ac_c_conftest_c11_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} +${ac_c_conftest_c11_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + ${ac_c_conftest_c11_main} + return ok; +} +" + +# Test code for whether the C compiler supports C99 (complete). +ac_c_conftest_c99_program="${ac_c_conftest_c89_globals} +${ac_c_conftest_c99_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + ${ac_c_conftest_c99_main} + return ok; +} +" + +# Test code for whether the C compiler supports C89 (complete). +ac_c_conftest_c89_program="${ac_c_conftest_c89_globals} + +int +main (int argc, char **argv) +{ + int ok = 0; + ${ac_c_conftest_c89_main} + return ok; +} +" + +# Check that the precious variables saved in the cache have kept the same +# value. +ac_cache_corrupted=false +for ac_var in $ac_precious_vars; do + eval ac_old_set=\$ac_cv_env_${ac_var}_set + eval ac_new_set=\$ac_env_${ac_var}_set + eval ac_old_val=\$ac_cv_env_${ac_var}_value + eval ac_new_val=\$ac_env_${ac_var}_value + case $ac_old_set,$ac_new_set in + set,) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was set to \`$ac_old_val' in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,set) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' was not set in the previous run" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' was not set in the previous run" >&2;} + ac_cache_corrupted=: ;; + ,);; + *) + if test "x$ac_old_val" != "x$ac_new_val"; then + # differences in whitespace do not lead to failure. + ac_old_val_w=`echo x $ac_old_val` + ac_new_val_w=`echo x $ac_new_val` + if test "$ac_old_val_w" != "$ac_new_val_w"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: \`$ac_var' has changed since the previous run:" >&5 +printf "%s\n" "$as_me: error: \`$ac_var' has changed since the previous run:" >&2;} + ac_cache_corrupted=: + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&5 +printf "%s\n" "$as_me: warning: ignoring whitespace changes in \`$ac_var' since the previous run:" >&2;} + eval $ac_var=\$ac_old_val + fi + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: former value: \`$ac_old_val'" >&5 +printf "%s\n" "$as_me: former value: \`$ac_old_val'" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: current value: \`$ac_new_val'" >&5 +printf "%s\n" "$as_me: current value: \`$ac_new_val'" >&2;} + fi;; + esac + # Pass precious variables to config.status. + if test "$ac_new_set" = set; then + case $ac_new_val in + *\'*) ac_arg=$ac_var=`printf "%s\n" "$ac_new_val" | sed "s/'/'\\\\\\\\''/g"` ;; + *) ac_arg=$ac_var=$ac_new_val ;; + esac + case " $ac_configure_args " in + *" '$ac_arg' "*) ;; # Avoid dups. Use of quotes ensures accuracy. + *) as_fn_append ac_configure_args " '$ac_arg'" ;; + esac + fi +done +if $ac_cache_corrupted; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: changes in the environment can compromise the build" >&5 +printf "%s\n" "$as_me: error: changes in the environment can compromise the build" >&2;} + as_fn_error $? "run \`${MAKE-make} distclean' and/or \`rm $cache_file' + and start over" "$LINENO" 5 +fi +## -------------------- ## +## Main body of script. ## +## -------------------- ## + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + + + + + + + + + + + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu +if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}gcc", so it can be a program name with args. +set dummy ${ac_tool_prefix}gcc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}gcc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "gcc", so it can be a program name with args. +set dummy gcc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="gcc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}cc", so it can be a program name with args. +set dummy ${ac_tool_prefix}cc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}cc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + fi +fi +if test -z "$CC"; then + # Extract the first word of "cc", so it can be a program name with args. +set dummy cc; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else + ac_prog_rejected=no +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + if test "$as_dir$ac_word$ac_exec_ext" = "/usr/ucb/cc"; then + ac_prog_rejected=yes + continue + fi + ac_cv_prog_CC="cc" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +if test $ac_prog_rejected = yes; then + # We found a bogon in the path, so make sure we never use it. + set dummy $ac_cv_prog_CC + shift + if test $# != 0; then + # We chose a different compiler from the bogus one. + # However, it has the same basename, so the bogon will be chosen + # first if we set CC to just the basename; use the full file name. + shift + ac_cv_prog_CC="$as_dir$ac_word${1+' '}$@" + fi +fi +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + for ac_prog in cl.exe + do + # Extract the first word of "$ac_tool_prefix$ac_prog", so it can be a program name with args. +set dummy $ac_tool_prefix$ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="$ac_tool_prefix$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$CC" && break + done +fi +if test -z "$CC"; then + ac_ct_CC=$CC + for ac_prog in cl.exe +do + # Extract the first word of "$ac_prog", so it can be a program name with args. +set dummy $ac_prog; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="$ac_prog" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + + test -n "$ac_ct_CC" && break +done + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +fi + +fi +if test -z "$CC"; then + if test -n "$ac_tool_prefix"; then + # Extract the first word of "${ac_tool_prefix}clang", so it can be a program name with args. +set dummy ${ac_tool_prefix}clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$CC"; then + ac_cv_prog_CC="$CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_CC="${ac_tool_prefix}clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +CC=$ac_cv_prog_CC +if test -n "$CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $CC" >&5 +printf "%s\n" "$CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + +fi +if test -z "$ac_cv_prog_CC"; then + ac_ct_CC=$CC + # Extract the first word of "clang", so it can be a program name with args. +set dummy clang; ac_word=$2 +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $ac_word" >&5 +printf %s "checking for $ac_word... " >&6; } +if test ${ac_cv_prog_ac_ct_CC+y} +then : + printf %s "(cached) " >&6 +else $as_nop + if test -n "$ac_ct_CC"; then + ac_cv_prog_ac_ct_CC="$ac_ct_CC" # Let the user override the test. +else +as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + for ac_exec_ext in '' $ac_executable_extensions; do + if as_fn_executable_p "$as_dir$ac_word$ac_exec_ext"; then + ac_cv_prog_ac_ct_CC="clang" + printf "%s\n" "$as_me:${as_lineno-$LINENO}: found $as_dir$ac_word$ac_exec_ext" >&5 + break 2 + fi +done + done +IFS=$as_save_IFS + +fi +fi +ac_ct_CC=$ac_cv_prog_ac_ct_CC +if test -n "$ac_ct_CC"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_ct_CC" >&5 +printf "%s\n" "$ac_ct_CC" >&6; } +else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +fi + + if test "x$ac_ct_CC" = x; then + CC="" + else + case $cross_compiling:$ac_tool_warned in +yes:) +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: using cross tools not prefixed with host triplet" >&5 +printf "%s\n" "$as_me: WARNING: using cross tools not prefixed with host triplet" >&2;} +ac_tool_warned=yes ;; +esac + CC=$ac_ct_CC + fi +else + CC="$ac_cv_prog_CC" +fi + +fi + + +test -z "$CC" && { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "no acceptable C compiler found in \$PATH +See \`config.log' for more details" "$LINENO" 5; } + +# Provide some information about the compiler. +printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler version" >&5 +set X $ac_compile +ac_compiler=$2 +for ac_option in --version -v -V -qversion -version; do + { { ac_try="$ac_compiler $ac_option >&5" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compiler $ac_option >&5") 2>conftest.err + ac_status=$? + if test -s conftest.err; then + sed '10a\ +... rest of stderr output deleted ... + 10q' conftest.err >conftest.er1 + cat conftest.er1 >&5 + fi + rm -f conftest.er1 conftest.err + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +done + +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files a.out a.out.dSYM a.exe b.out" +# Try to create an executable without -o first, disregard a.out. +# It will help us diagnose broken compilers, and finding out an intuition +# of exeext. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the C compiler works" >&5 +printf %s "checking whether the C compiler works... " >&6; } +ac_link_default=`printf "%s\n" "$ac_link" | sed 's/ -o *conftest[^ ]*//'` + +# The possible output files: +ac_files="a.out conftest.exe conftest a.exe a_out.exe b.out conftest.*" + +ac_rmfiles= +for ac_file in $ac_files +do + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + * ) ac_rmfiles="$ac_rmfiles $ac_file";; + esac +done +rm -f $ac_rmfiles + +if { { ac_try="$ac_link_default" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link_default") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + # Autoconf-2.13 could set the ac_cv_exeext variable to `no'. +# So ignore a value of `no', otherwise this would lead to `EXEEXT = no' +# in a Makefile. We should not override ac_cv_exeext if it was cached, +# so that the user can short-circuit this test for compilers unknown to +# Autoconf. +for ac_file in $ac_files '' +do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) + ;; + [ab].out ) + # We found the default executable, but exeext='' is most + # certainly right. + break;; + *.* ) + if test ${ac_cv_exeext+y} && test "$ac_cv_exeext" != no; + then :; else + ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + fi + # We set ac_cv_exeext here because the later test for it is not + # safe: cross compilers may not add the suffix if given an `-o' + # argument, so we may need to know it at that point already. + # Even if this section looks crufty: it has the advantage of + # actually working. + break;; + * ) + break;; + esac +done +test "$ac_cv_exeext" = no && ac_cv_exeext= + +else $as_nop + ac_file='' +fi +if test -z "$ac_file" +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: no" >&5 +printf "%s\n" "no" >&6; } +printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "C compiler cannot create executables +See \`config.log' for more details" "$LINENO" 5; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: yes" >&5 +printf "%s\n" "yes" >&6; } +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for C compiler default output file name" >&5 +printf %s "checking for C compiler default output file name... " >&6; } +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_file" >&5 +printf "%s\n" "$ac_file" >&6; } +ac_exeext=$ac_cv_exeext + +rm -f -r a.out a.out.dSYM a.exe conftest$ac_cv_exeext b.out +ac_clean_files=$ac_clean_files_save +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of executables" >&5 +printf %s "checking for suffix of executables... " >&6; } +if { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + # If both `conftest.exe' and `conftest' are `present' (well, observable) +# catch `conftest.exe'. For instance with Cygwin, `ls conftest' will +# work properly (i.e., refer to `conftest.exe'), while it won't with +# `rm'. +for ac_file in conftest.exe conftest conftest.*; do + test -f "$ac_file" || continue + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM | *.o | *.obj ) ;; + *.* ) ac_cv_exeext=`expr "$ac_file" : '[^.]*\(\..*\)'` + break;; + * ) break;; + esac +done +else $as_nop + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of executables: cannot compile and link +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest conftest$ac_cv_exeext +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_exeext" >&5 +printf "%s\n" "$ac_cv_exeext" >&6; } + +rm -f conftest.$ac_ext +EXEEXT=$ac_cv_exeext +ac_exeext=$EXEEXT +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +#include +int +main (void) +{ +FILE *f = fopen ("conftest.out", "w"); + return ferror (f) || fclose (f) != 0; + + ; + return 0; +} +_ACEOF +ac_clean_files="$ac_clean_files conftest.out" +# Check that the compiler produces executables we can run. If not, either +# the compiler is broken, or we cross compile. +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether we are cross compiling" >&5 +printf %s "checking whether we are cross compiling... " >&6; } +if test "$cross_compiling" != yes; then + { { ac_try="$ac_link" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_link") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } + if { ac_try='./conftest$ac_cv_exeext' + { { case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_try") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; }; }; then + cross_compiling=no + else + if test "$cross_compiling" = maybe; then + cross_compiling=yes + else + { { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error 77 "cannot run C compiled programs. +If you meant to cross compile, use \`--host'. +See \`config.log' for more details" "$LINENO" 5; } + fi + fi +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $cross_compiling" >&5 +printf "%s\n" "$cross_compiling" >&6; } + +rm -f conftest.$ac_ext conftest$ac_cv_exeext conftest.out +ac_clean_files=$ac_clean_files_save +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for suffix of object files" >&5 +printf %s "checking for suffix of object files... " >&6; } +if test ${ac_cv_objext+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +rm -f conftest.o conftest.obj +if { { ac_try="$ac_compile" +case "(($ac_try" in + *\"* | *\`* | *\\*) ac_try_echo=\$ac_try;; + *) ac_try_echo=$ac_try;; +esac +eval ac_try_echo="\"\$as_me:${as_lineno-$LINENO}: $ac_try_echo\"" +printf "%s\n" "$ac_try_echo"; } >&5 + (eval "$ac_compile") 2>&5 + ac_status=$? + printf "%s\n" "$as_me:${as_lineno-$LINENO}: \$? = $ac_status" >&5 + test $ac_status = 0; } +then : + for ac_file in conftest.o conftest.obj conftest.*; do + test -f "$ac_file" || continue; + case $ac_file in + *.$ac_ext | *.xcoff | *.tds | *.d | *.pdb | *.xSYM | *.bb | *.bbg | *.map | *.inf | *.dSYM ) ;; + *) ac_cv_objext=`expr "$ac_file" : '.*\.\(.*\)'` + break;; + esac +done +else $as_nop + printf "%s\n" "$as_me: failed program was:" >&5 +sed 's/^/| /' conftest.$ac_ext >&5 + +{ { printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: in \`$ac_pwd':" >&5 +printf "%s\n" "$as_me: error: in \`$ac_pwd':" >&2;} +as_fn_error $? "cannot compute suffix of object files: cannot compile +See \`config.log' for more details" "$LINENO" 5; } +fi +rm -f conftest.$ac_cv_objext conftest.$ac_ext +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_objext" >&5 +printf "%s\n" "$ac_cv_objext" >&6; } +OBJEXT=$ac_cv_objext +ac_objext=$OBJEXT +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether the compiler supports GNU C" >&5 +printf %s "checking whether the compiler supports GNU C... " >&6; } +if test ${ac_cv_c_compiler_gnu+y} +then : + printf %s "(cached) " >&6 +else $as_nop + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ +#ifndef __GNUC__ + choke me +#endif + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_compiler_gnu=yes +else $as_nop + ac_compiler_gnu=no +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +ac_cv_c_compiler_gnu=$ac_compiler_gnu + +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_c_compiler_gnu" >&5 +printf "%s\n" "$ac_cv_c_compiler_gnu" >&6; } +ac_compiler_gnu=$ac_cv_c_compiler_gnu + +if test $ac_compiler_gnu = yes; then + GCC=yes +else + GCC= +fi +ac_test_CFLAGS=${CFLAGS+y} +ac_save_CFLAGS=$CFLAGS +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking whether $CC accepts -g" >&5 +printf %s "checking whether $CC accepts -g... " >&6; } +if test ${ac_cv_prog_cc_g+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_save_c_werror_flag=$ac_c_werror_flag + ac_c_werror_flag=yes + ac_cv_prog_cc_g=no + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +else $as_nop + CFLAGS="" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + +else $as_nop + ac_c_werror_flag=$ac_save_c_werror_flag + CFLAGS="-g" + cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ + +int +main (void) +{ + + ; + return 0; +} +_ACEOF +if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_g=yes +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam conftest.$ac_ext + ac_c_werror_flag=$ac_save_c_werror_flag +fi +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_g" >&5 +printf "%s\n" "$ac_cv_prog_cc_g" >&6; } +if test $ac_test_CFLAGS; then + CFLAGS=$ac_save_CFLAGS +elif test $ac_cv_prog_cc_g = yes; then + if test "$GCC" = yes; then + CFLAGS="-g -O2" + else + CFLAGS="-g" + fi +else + if test "$GCC" = yes; then + CFLAGS="-O2" + else + CFLAGS= + fi +fi +ac_prog_cc_stdc=no +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C11 features" >&5 +printf %s "checking for $CC option to enable C11 features... " >&6; } +if test ${ac_cv_prog_cc_c11+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c11=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c11_program +_ACEOF +for ac_arg in '' -std=gnu11 +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c11=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c11" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c11" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c11" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c11" >&5 +printf "%s\n" "$ac_cv_prog_cc_c11" >&6; } + CC="$CC $ac_cv_prog_cc_c11" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c11 + ac_prog_cc_stdc=c11 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C99 features" >&5 +printf %s "checking for $CC option to enable C99 features... " >&6; } +if test ${ac_cv_prog_cc_c99+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c99=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c99_program +_ACEOF +for ac_arg in '' -std=gnu99 -std=c99 -c99 -qlanglvl=extc1x -qlanglvl=extc99 -AC99 -D_STDC_C99= +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c99=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c99" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c99" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c99" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c99" >&5 +printf "%s\n" "$ac_cv_prog_cc_c99" >&6; } + CC="$CC $ac_cv_prog_cc_c99" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c99 + ac_prog_cc_stdc=c99 +fi +fi +if test x$ac_prog_cc_stdc = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking for $CC option to enable C89 features" >&5 +printf %s "checking for $CC option to enable C89 features... " >&6; } +if test ${ac_cv_prog_cc_c89+y} +then : + printf %s "(cached) " >&6 +else $as_nop + ac_cv_prog_cc_c89=no +ac_save_CC=$CC +cat confdefs.h - <<_ACEOF >conftest.$ac_ext +/* end confdefs.h. */ +$ac_c_conftest_c89_program +_ACEOF +for ac_arg in '' -qlanglvl=extc89 -qlanglvl=ansi -std -Ae "-Aa -D_HPUX_SOURCE" "-Xc -D__EXTENSIONS__" +do + CC="$ac_save_CC $ac_arg" + if ac_fn_c_try_compile "$LINENO" +then : + ac_cv_prog_cc_c89=$ac_arg +fi +rm -f core conftest.err conftest.$ac_objext conftest.beam + test "x$ac_cv_prog_cc_c89" != "xno" && break +done +rm -f conftest.$ac_ext +CC=$ac_save_CC +fi + +if test "x$ac_cv_prog_cc_c89" = xno +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: unsupported" >&5 +printf "%s\n" "unsupported" >&6; } +else $as_nop + if test "x$ac_cv_prog_cc_c89" = x +then : + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: none needed" >&5 +printf "%s\n" "none needed" >&6; } +else $as_nop + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: result: $ac_cv_prog_cc_c89" >&5 +printf "%s\n" "$ac_cv_prog_cc_c89" >&6; } + CC="$CC $ac_cv_prog_cc_c89" +fi + ac_cv_prog_cc_stdc=$ac_cv_prog_cc_c89 + ac_prog_cc_stdc=c89 +fi +fi + +ac_ext=c +ac_cpp='$CPP $CPPFLAGS' +ac_compile='$CC -c $CFLAGS $CPPFLAGS conftest.$ac_ext >&5' +ac_link='$CC -o conftest$ac_exeext $CFLAGS $CPPFLAGS $LDFLAGS conftest.$ac_ext $LIBS >&5' +ac_compiler_gnu=$ac_cv_c_compiler_gnu + + +printf "%s\n" "#define _XOPEN_SOURCE 700" >>confdefs.h + + +printf "%s\n" "#define YP_VERSION_MAJOR 0" >>confdefs.h + + +printf "%s\n" "#define YP_VERSION_MINOR 4" >>confdefs.h + + +printf "%s\n" "#define YP_VERSION_PATCH 0" >>confdefs.h + + +printf "%s\n" "#define YP_VERSION \"0.4.0\"" >>confdefs.h + + + +ac_fn_c_check_func "$LINENO" "mmap" "ac_cv_func_mmap" +if test "x$ac_cv_func_mmap" = xyes +then : + printf "%s\n" "#define HAVE_MMAP 1" >>confdefs.h + +fi + +ac_fn_c_check_func "$LINENO" "snprintf" "ac_cv_func_snprintf" +if test "x$ac_cv_func_snprintf" = xyes +then : + printf "%s\n" "#define HAVE_SNPRINTF 1" >>confdefs.h + +fi + +ac_fn_c_check_func "$LINENO" "strncasecmp" "ac_cv_func_strncasecmp" +if test "x$ac_cv_func_strncasecmp" = xyes +then : + printf "%s\n" "#define HAVE_STRNCASECMP 1" >>confdefs.h + +fi + + +ac_config_headers="$ac_config_headers include/yarp/config.h:config.h.in" + +ac_config_files="$ac_config_files Makefile" + +cat >confcache <<\_ACEOF +# This file is a shell script that caches the results of configure +# tests run on this system so they can be shared between configure +# scripts and configure runs, see configure's option --config-cache. +# It is not useful on other systems. If it contains results you don't +# want to keep, you may remove or edit it. +# +# config.status only pays attention to the cache file if you give it +# the --recheck option to rerun configure. +# +# `ac_cv_env_foo' variables (set or unset) will be overridden when +# loading this file, other *unset* `ac_cv_foo' will be assigned the +# following values. + +_ACEOF + +# The following way of writing the cache mishandles newlines in values, +# but we know of no workaround that is simple, portable, and efficient. +# So, we kill variables containing newlines. +# Ultrix sh set writes to stderr and can't be redirected directly, +# and sets the high bit in the cache file unless we assign to the vars. +( + for ac_var in `(set) 2>&1 | sed -n 's/^\([a-zA-Z_][a-zA-Z0-9_]*\)=.*/\1/p'`; do + eval ac_val=\$$ac_var + case $ac_val in #( + *${as_nl}*) + case $ac_var in #( + *_cv_*) { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: cache variable $ac_var contains a newline" >&5 +printf "%s\n" "$as_me: WARNING: cache variable $ac_var contains a newline" >&2;} ;; + esac + case $ac_var in #( + _ | IFS | as_nl) ;; #( + BASH_ARGV | BASH_SOURCE) eval $ac_var= ;; #( + *) { eval $ac_var=; unset $ac_var;} ;; + esac ;; + esac + done + + (set) 2>&1 | + case $as_nl`(ac_space=' '; set) 2>&1` in #( + *${as_nl}ac_space=\ *) + # `set' does not quote correctly, so add quotes: double-quote + # substitution turns \\\\ into \\, and sed turns \\ into \. + sed -n \ + "s/'/'\\\\''/g; + s/^\\([_$as_cr_alnum]*_cv_[_$as_cr_alnum]*\\)=\\(.*\\)/\\1='\\2'/p" + ;; #( + *) + # `set' quotes correctly as required by POSIX, so do not add quotes. + sed -n "/^[_$as_cr_alnum]*_cv_[_$as_cr_alnum]*=/p" + ;; + esac | + sort +) | + sed ' + /^ac_cv_env_/b end + t clear + :clear + s/^\([^=]*\)=\(.*[{}].*\)$/test ${\1+y} || &/ + t end + s/^\([^=]*\)=\(.*\)$/\1=${\1=\2}/ + :end' >>confcache +if diff "$cache_file" confcache >/dev/null 2>&1; then :; else + if test -w "$cache_file"; then + if test "x$cache_file" != "x/dev/null"; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: updating cache $cache_file" >&5 +printf "%s\n" "$as_me: updating cache $cache_file" >&6;} + if test ! -f "$cache_file" || test -h "$cache_file"; then + cat confcache >"$cache_file" + else + case $cache_file in #( + */* | ?:*) + mv -f confcache "$cache_file"$$ && + mv -f "$cache_file"$$ "$cache_file" ;; #( + *) + mv -f confcache "$cache_file" ;; + esac + fi + fi + else + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: not updating unwritable cache $cache_file" >&5 +printf "%s\n" "$as_me: not updating unwritable cache $cache_file" >&6;} + fi +fi +rm -f confcache + +test "x$prefix" = xNONE && prefix=$ac_default_prefix +# Let make expand exec_prefix. +test "x$exec_prefix" = xNONE && exec_prefix='${prefix}' + +DEFS=-DHAVE_CONFIG_H + +ac_libobjs= +ac_ltlibobjs= +U= +for ac_i in : $LIBOBJS; do test "x$ac_i" = x: && continue + # 1. Remove the extension, and $U if already installed. + ac_script='s/\$U\././;s/\.o$//;s/\.obj$//' + ac_i=`printf "%s\n" "$ac_i" | sed "$ac_script"` + # 2. Prepend LIBOBJDIR. When used with automake>=1.10 LIBOBJDIR + # will be set to the directory where LIBOBJS objects are built. + as_fn_append ac_libobjs " \${LIBOBJDIR}$ac_i\$U.$ac_objext" + as_fn_append ac_ltlibobjs " \${LIBOBJDIR}$ac_i"'$U.lo' +done +LIBOBJS=$ac_libobjs + +LTLIBOBJS=$ac_ltlibobjs + + + +: "${CONFIG_STATUS=./config.status}" +ac_write_fail=0 +ac_clean_files_save=$ac_clean_files +ac_clean_files="$ac_clean_files $CONFIG_STATUS" +{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $CONFIG_STATUS" >&5 +printf "%s\n" "$as_me: creating $CONFIG_STATUS" >&6;} +as_write_fail=0 +cat >$CONFIG_STATUS <<_ASEOF || as_write_fail=1 +#! $SHELL +# Generated by $as_me. +# Run this file to recreate the current configuration. +# Compiler output produced by configure, useful for debugging +# configure, is in config.log if it exists. + +debug=false +ac_cs_recheck=false +ac_cs_silent=false + +SHELL=\${CONFIG_SHELL-$SHELL} +export SHELL +_ASEOF +cat >>$CONFIG_STATUS <<\_ASEOF || as_write_fail=1 +## -------------------- ## +## M4sh Initialization. ## +## -------------------- ## + +# Be more Bourne compatible +DUALCASE=1; export DUALCASE # for MKS sh +as_nop=: +if test ${ZSH_VERSION+y} && (emulate sh) >/dev/null 2>&1 +then : + emulate sh + NULLCMD=: + # Pre-4.2 versions of Zsh do word splitting on ${1+"$@"}, which + # is contrary to our usage. Disable this feature. + alias -g '${1+"$@"}'='"$@"' + setopt NO_GLOB_SUBST +else $as_nop + case `(set -o) 2>/dev/null` in #( + *posix*) : + set -o posix ;; #( + *) : + ;; +esac +fi + + + +# Reset variables that may have inherited troublesome values from +# the environment. + +# IFS needs to be set, to space, tab, and newline, in precisely that order. +# (If _AS_PATH_WALK were called with IFS unset, it would have the +# side effect of setting IFS to empty, thus disabling word splitting.) +# Quoting is to prevent editors from complaining about space-tab. +as_nl=' +' +export as_nl +IFS=" "" $as_nl" + +PS1='$ ' +PS2='> ' +PS4='+ ' + +# Ensure predictable behavior from utilities with locale-dependent output. +LC_ALL=C +export LC_ALL +LANGUAGE=C +export LANGUAGE + +# We cannot yet rely on "unset" to work, but we need these variables +# to be unset--not just set to an empty or harmless value--now, to +# avoid bugs in old shells (e.g. pre-3.0 UWIN ksh). This construct +# also avoids known problems related to "unset" and subshell syntax +# in other old shells (e.g. bash 2.01 and pdksh 5.2.14). +for as_var in BASH_ENV ENV MAIL MAILPATH CDPATH +do eval test \${$as_var+y} \ + && ( (unset $as_var) || exit 1) >/dev/null 2>&1 && unset $as_var || : +done + +# Ensure that fds 0, 1, and 2 are open. +if (exec 3>&0) 2>/dev/null; then :; else exec 0&1) 2>/dev/null; then :; else exec 1>/dev/null; fi +if (exec 3>&2) ; then :; else exec 2>/dev/null; fi + +# The user is always right. +if ${PATH_SEPARATOR+false} :; then + PATH_SEPARATOR=: + (PATH='/bin;/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 && { + (PATH='/bin:/bin'; FPATH=$PATH; sh -c :) >/dev/null 2>&1 || + PATH_SEPARATOR=';' + } +fi + + +# Find who we are. Look in the path if we contain no directory separator. +as_myself= +case $0 in #(( + *[\\/]* ) as_myself=$0 ;; + *) as_save_IFS=$IFS; IFS=$PATH_SEPARATOR +for as_dir in $PATH +do + IFS=$as_save_IFS + case $as_dir in #((( + '') as_dir=./ ;; + */) ;; + *) as_dir=$as_dir/ ;; + esac + test -r "$as_dir$0" && as_myself=$as_dir$0 && break + done +IFS=$as_save_IFS + + ;; +esac +# We did not find ourselves, most probably we were run as `sh COMMAND' +# in which case we are not to be found in the path. +if test "x$as_myself" = x; then + as_myself=$0 +fi +if test ! -f "$as_myself"; then + printf "%s\n" "$as_myself: error: cannot find myself; rerun with an absolute file name" >&2 + exit 1 +fi + + + +# as_fn_error STATUS ERROR [LINENO LOG_FD] +# ---------------------------------------- +# Output "`basename $0`: error: ERROR" to stderr. If LINENO and LOG_FD are +# provided, also output the error to LOG_FD, referencing LINENO. Then exit the +# script with STATUS, using 1 if that was 0. +as_fn_error () +{ + as_status=$1; test $as_status -eq 0 && as_status=1 + if test "$4"; then + as_lineno=${as_lineno-"$3"} as_lineno_stack=as_lineno_stack=$as_lineno_stack + printf "%s\n" "$as_me:${as_lineno-$LINENO}: error: $2" >&$4 + fi + printf "%s\n" "$as_me: error: $2" >&2 + as_fn_exit $as_status +} # as_fn_error + + + +# as_fn_set_status STATUS +# ----------------------- +# Set $? to STATUS, without forking. +as_fn_set_status () +{ + return $1 +} # as_fn_set_status + +# as_fn_exit STATUS +# ----------------- +# Exit the shell with STATUS, even in a "trap 0" or "set -e" context. +as_fn_exit () +{ + set +e + as_fn_set_status $1 + exit $1 +} # as_fn_exit + +# as_fn_unset VAR +# --------------- +# Portably unset VAR. +as_fn_unset () +{ + { eval $1=; unset $1;} +} +as_unset=as_fn_unset + +# as_fn_append VAR VALUE +# ---------------------- +# Append the text in VALUE to the end of the definition contained in VAR. Take +# advantage of any shell optimizations that allow amortized linear growth over +# repeated appends, instead of the typical quadratic growth present in naive +# implementations. +if (eval "as_var=1; as_var+=2; test x\$as_var = x12") 2>/dev/null +then : + eval 'as_fn_append () + { + eval $1+=\$2 + }' +else $as_nop + as_fn_append () + { + eval $1=\$$1\$2 + } +fi # as_fn_append + +# as_fn_arith ARG... +# ------------------ +# Perform arithmetic evaluation on the ARGs, and store the result in the +# global $as_val. Take advantage of shells that can avoid forks. The arguments +# must be portable across $(()) and expr. +if (eval "test \$(( 1 + 1 )) = 2") 2>/dev/null +then : + eval 'as_fn_arith () + { + as_val=$(( $* )) + }' +else $as_nop + as_fn_arith () + { + as_val=`expr "$@" || test $? -eq 1` + } +fi # as_fn_arith + + +if expr a : '\(a\)' >/dev/null 2>&1 && + test "X`expr 00001 : '.*\(...\)'`" = X001; then + as_expr=expr +else + as_expr=false +fi + +if (basename -- /) >/dev/null 2>&1 && test "X`basename -- / 2>&1`" = "X/"; then + as_basename=basename +else + as_basename=false +fi + +if (as_dir=`dirname -- /` && test "X$as_dir" = X/) >/dev/null 2>&1; then + as_dirname=dirname +else + as_dirname=false +fi + +as_me=`$as_basename -- "$0" || +$as_expr X/"$0" : '.*/\([^/][^/]*\)/*$' \| \ + X"$0" : 'X\(//\)$' \| \ + X"$0" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X/"$0" | + sed '/^.*\/\([^/][^/]*\)\/*$/{ + s//\1/ + q + } + /^X\/\(\/\/\)$/{ + s//\1/ + q + } + /^X\/\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + +# Avoid depending upon Character Ranges. +as_cr_letters='abcdefghijklmnopqrstuvwxyz' +as_cr_LETTERS='ABCDEFGHIJKLMNOPQRSTUVWXYZ' +as_cr_Letters=$as_cr_letters$as_cr_LETTERS +as_cr_digits='0123456789' +as_cr_alnum=$as_cr_Letters$as_cr_digits + + +# Determine whether it's possible to make 'echo' print without a newline. +# These variables are no longer used directly by Autoconf, but are AC_SUBSTed +# for compatibility with existing Makefiles. +ECHO_C= ECHO_N= ECHO_T= +case `echo -n x` in #((((( +-n*) + case `echo 'xy\c'` in + *c*) ECHO_T=' ';; # ECHO_T is single tab character. + xy) ECHO_C='\c';; + *) echo `echo ksh88 bug on AIX 6.1` > /dev/null + ECHO_T=' ';; + esac;; +*) + ECHO_N='-n';; +esac + +# For backward compatibility with old third-party macros, we provide +# the shell variables $as_echo and $as_echo_n. New code should use +# AS_ECHO(["message"]) and AS_ECHO_N(["message"]), respectively. +as_echo='printf %s\n' +as_echo_n='printf %s' + +rm -f conf$$ conf$$.exe conf$$.file +if test -d conf$$.dir; then + rm -f conf$$.dir/conf$$.file +else + rm -f conf$$.dir + mkdir conf$$.dir 2>/dev/null +fi +if (echo >conf$$.file) 2>/dev/null; then + if ln -s conf$$.file conf$$ 2>/dev/null; then + as_ln_s='ln -s' + # ... but there are two gotchas: + # 1) On MSYS, both `ln -s file dir' and `ln file dir' fail. + # 2) DJGPP < 2.04 has no symlinks; `ln -s' creates a wrapper executable. + # In both cases, we have to default to `cp -pR'. + ln -s conf$$.file conf$$.dir 2>/dev/null && test ! -f conf$$.exe || + as_ln_s='cp -pR' + elif ln conf$$.file conf$$ 2>/dev/null; then + as_ln_s=ln + else + as_ln_s='cp -pR' + fi +else + as_ln_s='cp -pR' +fi +rm -f conf$$ conf$$.exe conf$$.dir/conf$$.file conf$$.file +rmdir conf$$.dir 2>/dev/null + + +# as_fn_mkdir_p +# ------------- +# Create "$as_dir" as a directory, including parents if necessary. +as_fn_mkdir_p () +{ + + case $as_dir in #( + -*) as_dir=./$as_dir;; + esac + test -d "$as_dir" || eval $as_mkdir_p || { + as_dirs= + while :; do + case $as_dir in #( + *\'*) as_qdir=`printf "%s\n" "$as_dir" | sed "s/'/'\\\\\\\\''/g"`;; #'( + *) as_qdir=$as_dir;; + esac + as_dirs="'$as_qdir' $as_dirs" + as_dir=`$as_dirname -- "$as_dir" || +$as_expr X"$as_dir" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$as_dir" : 'X\(//\)[^/]' \| \ + X"$as_dir" : 'X\(//\)$' \| \ + X"$as_dir" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$as_dir" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + test -d "$as_dir" && break + done + test -z "$as_dirs" || eval "mkdir $as_dirs" + } || test -d "$as_dir" || as_fn_error $? "cannot create directory $as_dir" + + +} # as_fn_mkdir_p +if mkdir -p . 2>/dev/null; then + as_mkdir_p='mkdir -p "$as_dir"' +else + test -d ./-p && rmdir ./-p + as_mkdir_p=false +fi + + +# as_fn_executable_p FILE +# ----------------------- +# Test if FILE is an executable regular file. +as_fn_executable_p () +{ + test -f "$1" && test -x "$1" +} # as_fn_executable_p +as_test_x='test -x' +as_executable_p=as_fn_executable_p + +# Sed expression to map a string onto a valid CPP name. +as_tr_cpp="eval sed 'y%*$as_cr_letters%P$as_cr_LETTERS%;s%[^_$as_cr_alnum]%_%g'" + +# Sed expression to map a string onto a valid variable name. +as_tr_sh="eval sed 'y%*+%pp%;s%[^_$as_cr_alnum]%_%g'" + + +exec 6>&1 +## ----------------------------------- ## +## Main body of $CONFIG_STATUS script. ## +## ----------------------------------- ## +_ASEOF +test $as_write_fail = 0 && chmod +x $CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# Save the log message, to keep $0 and so on meaningful, and to +# report actual input values of CONFIG_FILES etc. instead of their +# values after options handling. +ac_log=" +This file was extended by YARP $as_me 0.4.0, which was +generated by GNU Autoconf 2.71. Invocation command line was + + CONFIG_FILES = $CONFIG_FILES + CONFIG_HEADERS = $CONFIG_HEADERS + CONFIG_LINKS = $CONFIG_LINKS + CONFIG_COMMANDS = $CONFIG_COMMANDS + $ $0 $@ + +on `(hostname || uname -n) 2>/dev/null | sed 1q` +" + +_ACEOF + +case $ac_config_files in *" +"*) set x $ac_config_files; shift; ac_config_files=$*;; +esac + +case $ac_config_headers in *" +"*) set x $ac_config_headers; shift; ac_config_headers=$*;; +esac + + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +# Files that config.status was made for. +config_files="$ac_config_files" +config_headers="$ac_config_headers" + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +ac_cs_usage="\ +\`$as_me' instantiates files and other configuration actions +from templates according to the current configuration. Unless the files +and actions are specified as TAGs, all are instantiated by default. + +Usage: $0 [OPTION]... [TAG]... + + -h, --help print this help, then exit + -V, --version print version number and configuration settings, then exit + --config print configuration, then exit + -q, --quiet, --silent + do not print progress messages + -d, --debug don't remove temporary files + --recheck update $as_me by reconfiguring in the same conditions + --file=FILE[:TEMPLATE] + instantiate the configuration file FILE + --header=FILE[:TEMPLATE] + instantiate the configuration header FILE + +Configuration files: +$config_files + +Configuration headers: +$config_headers + +Report bugs to . +YARP home page: ." + +_ACEOF +ac_cs_config=`printf "%s\n" "$ac_configure_args" | sed "$ac_safe_unquote"` +ac_cs_config_escaped=`printf "%s\n" "$ac_cs_config" | sed "s/^ //; s/'/'\\\\\\\\''/g"` +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_cs_config='$ac_cs_config_escaped' +ac_cs_version="\\ +YARP config.status 0.4.0 +configured by $0, generated by GNU Autoconf 2.71, + with options \\"\$ac_cs_config\\" + +Copyright (C) 2021 Free Software Foundation, Inc. +This config.status script is free software; the Free Software Foundation +gives unlimited permission to copy, distribute and modify it." + +ac_pwd='$ac_pwd' +srcdir='$srcdir' +test -n "\$AWK" || AWK=awk +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# The default lists apply if the user does not specify any file. +ac_need_defaults=: +while test $# != 0 +do + case $1 in + --*=?*) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg=`expr "X$1" : 'X[^=]*=\(.*\)'` + ac_shift=: + ;; + --*=) + ac_option=`expr "X$1" : 'X\([^=]*\)='` + ac_optarg= + ac_shift=: + ;; + *) + ac_option=$1 + ac_optarg=$2 + ac_shift=shift + ;; + esac + + case $ac_option in + # Handling of the options. + -recheck | --recheck | --rechec | --reche | --rech | --rec | --re | --r) + ac_cs_recheck=: ;; + --version | --versio | --versi | --vers | --ver | --ve | --v | -V ) + printf "%s\n" "$ac_cs_version"; exit ;; + --config | --confi | --conf | --con | --co | --c ) + printf "%s\n" "$ac_cs_config"; exit ;; + --debug | --debu | --deb | --de | --d | -d ) + debug=: ;; + --file | --fil | --fi | --f ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + '') as_fn_error $? "missing file argument" ;; + esac + as_fn_append CONFIG_FILES " '$ac_optarg'" + ac_need_defaults=false;; + --header | --heade | --head | --hea ) + $ac_shift + case $ac_optarg in + *\'*) ac_optarg=`printf "%s\n" "$ac_optarg" | sed "s/'/'\\\\\\\\''/g"` ;; + esac + as_fn_append CONFIG_HEADERS " '$ac_optarg'" + ac_need_defaults=false;; + --he | --h) + # Conflict between --help and --header + as_fn_error $? "ambiguous option: \`$1' +Try \`$0 --help' for more information.";; + --help | --hel | -h ) + printf "%s\n" "$ac_cs_usage"; exit ;; + -q | -quiet | --quiet | --quie | --qui | --qu | --q \ + | -silent | --silent | --silen | --sile | --sil | --si | --s) + ac_cs_silent=: ;; + + # This is an error. + -*) as_fn_error $? "unrecognized option: \`$1' +Try \`$0 --help' for more information." ;; + + *) as_fn_append ac_config_targets " $1" + ac_need_defaults=false ;; + + esac + shift +done + +ac_configure_extra_args= + +if $ac_cs_silent; then + exec 6>/dev/null + ac_configure_extra_args="$ac_configure_extra_args --silent" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +if \$ac_cs_recheck; then + set X $SHELL '$0' $ac_configure_args \$ac_configure_extra_args --no-create --no-recursion + shift + \printf "%s\n" "running CONFIG_SHELL=$SHELL \$*" >&6 + CONFIG_SHELL='$SHELL' + export CONFIG_SHELL + exec "\$@" +fi + +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +exec 5>>config.log +{ + echo + sed 'h;s/./-/g;s/^.../## /;s/...$/ ##/;p;x;p;x' <<_ASBOX +## Running $as_me. ## +_ASBOX + printf "%s\n" "$ac_log" +} >&5 + +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + +# Handling of arguments. +for ac_config_target in $ac_config_targets +do + case $ac_config_target in + "include/yarp/config.h") CONFIG_HEADERS="$CONFIG_HEADERS include/yarp/config.h:config.h.in" ;; + "Makefile") CONFIG_FILES="$CONFIG_FILES Makefile" ;; + + *) as_fn_error $? "invalid argument: \`$ac_config_target'" "$LINENO" 5;; + esac +done + + +# If the user did not use the arguments to specify the items to instantiate, +# then the envvar interface is used. Set only those that are not. +# We use the long form for the default assignment because of an extremely +# bizarre bug on SunOS 4.1.3. +if $ac_need_defaults; then + test ${CONFIG_FILES+y} || CONFIG_FILES=$config_files + test ${CONFIG_HEADERS+y} || CONFIG_HEADERS=$config_headers +fi + +# Have a temporary directory for convenience. Make it in the build tree +# simply because there is no reason against having it here, and in addition, +# creating and moving files from /tmp can sometimes cause problems. +# Hook for its removal unless debugging. +# Note that there is a small window in which the directory will not be cleaned: +# after its creation but before its name has been assigned to `$tmp'. +$debug || +{ + tmp= ac_tmp= + trap 'exit_status=$? + : "${ac_tmp:=$tmp}" + { test ! -d "$ac_tmp" || rm -fr "$ac_tmp"; } && exit $exit_status +' 0 + trap 'as_fn_exit 1' 1 2 13 15 +} +# Create a (secure) tmp directory for tmp files. + +{ + tmp=`(umask 077 && mktemp -d "./confXXXXXX") 2>/dev/null` && + test -d "$tmp" +} || +{ + tmp=./conf$$-$RANDOM + (umask 077 && mkdir "$tmp") +} || as_fn_error $? "cannot create a temporary directory in ." "$LINENO" 5 +ac_tmp=$tmp + +# Set up the scripts for CONFIG_FILES section. +# No need to generate them if there are no CONFIG_FILES. +# This happens for instance with `./config.status config.h'. +if test -n "$CONFIG_FILES"; then + + +ac_cr=`echo X | tr X '\015'` +# On cygwin, bash can eat \r inside `` if the user requested igncr. +# But we know of no other shell where ac_cr would be empty at this +# point, so we can use a bashism as a fallback. +if test "x$ac_cr" = x; then + eval ac_cr=\$\'\\r\' +fi +ac_cs_awk_cr=`$AWK 'BEGIN { print "a\rb" }' /dev/null` +if test "$ac_cs_awk_cr" = "a${ac_cr}b"; then + ac_cs_awk_cr='\\r' +else + ac_cs_awk_cr=$ac_cr +fi + +echo 'BEGIN {' >"$ac_tmp/subs1.awk" && +_ACEOF + + +{ + echo "cat >conf$$subs.awk <<_ACEOF" && + echo "$ac_subst_vars" | sed 's/.*/&!$&$ac_delim/' && + echo "_ACEOF" +} >conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 +ac_delim_num=`echo "$ac_subst_vars" | grep -c '^'` +ac_delim='%!_!# ' +for ac_last_try in false false false false false :; do + . ./conf$$subs.sh || + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + + ac_delim_n=`sed -n "s/.*$ac_delim\$/X/p" conf$$subs.awk | grep -c X` + if test $ac_delim_n = $ac_delim_num; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_STATUS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done +rm -f conf$$subs.sh + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +cat >>"\$ac_tmp/subs1.awk" <<\\_ACAWK && +_ACEOF +sed -n ' +h +s/^/S["/; s/!.*/"]=/ +p +g +s/^[^!]*!// +:repl +t repl +s/'"$ac_delim"'$// +t delim +:nl +h +s/\(.\{148\}\)..*/\1/ +t more1 +s/["\\]/\\&/g; s/^/"/; s/$/\\n"\\/ +p +n +b repl +:more1 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t nl +:delim +h +s/\(.\{148\}\)..*/\1/ +t more2 +s/["\\]/\\&/g; s/^/"/; s/$/"/ +p +b +:more2 +s/["\\]/\\&/g; s/^/"/; s/$/"\\/ +p +g +s/.\{148\}// +t delim +' >$CONFIG_STATUS || ac_write_fail=1 +rm -f conf$$subs.awk +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +_ACAWK +cat >>"\$ac_tmp/subs1.awk" <<_ACAWK && + for (key in S) S_is_set[key] = 1 + FS = "" + +} +{ + line = $ 0 + nfields = split(line, field, "@") + substed = 0 + len = length(field[1]) + for (i = 2; i < nfields; i++) { + key = field[i] + keylen = length(key) + if (S_is_set[key]) { + value = S[key] + line = substr(line, 1, len) "" value "" substr(line, len + keylen + 3) + len += length(value) + length(field[++i]) + substed = 1 + } else + len += 1 + keylen + } + + print line +} + +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +if sed "s/$ac_cr//" < /dev/null > /dev/null 2>&1; then + sed "s/$ac_cr\$//; s/$ac_cr/$ac_cs_awk_cr/g" +else + cat +fi < "$ac_tmp/subs1.awk" > "$ac_tmp/subs.awk" \ + || as_fn_error $? "could not setup config files machinery" "$LINENO" 5 +_ACEOF + +# VPATH may cause trouble with some makes, so we remove sole $(srcdir), +# ${srcdir} and @srcdir@ entries from VPATH if srcdir is ".", strip leading and +# trailing colons and then remove the whole line if VPATH becomes empty +# (actually we leave an empty line to preserve line numbers). +if test "x$srcdir" = x.; then + ac_vpsub='/^[ ]*VPATH[ ]*=[ ]*/{ +h +s/// +s/^/:/ +s/[ ]*$/:/ +s/:\$(srcdir):/:/g +s/:\${srcdir}:/:/g +s/:@srcdir@:/:/g +s/^:*// +s/:*$// +x +s/\(=[ ]*\).*/\1/ +G +s/\n// +s/^[^=]*=[ ]*$// +}' +fi + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +fi # test -n "$CONFIG_FILES" + +# Set up the scripts for CONFIG_HEADERS section. +# No need to generate them if there are no CONFIG_HEADERS. +# This happens for instance with `./config.status Makefile'. +if test -n "$CONFIG_HEADERS"; then +cat >"$ac_tmp/defines.awk" <<\_ACAWK || +BEGIN { +_ACEOF + +# Transform confdefs.h into an awk script `defines.awk', embedded as +# here-document in config.status, that substitutes the proper values into +# config.h.in to produce config.h. + +# Create a delimiter string that does not exist in confdefs.h, to ease +# handling of long lines. +ac_delim='%!_!# ' +for ac_last_try in false false :; do + ac_tt=`sed -n "/$ac_delim/p" confdefs.h` + if test -z "$ac_tt"; then + break + elif $ac_last_try; then + as_fn_error $? "could not make $CONFIG_HEADERS" "$LINENO" 5 + else + ac_delim="$ac_delim!$ac_delim _$ac_delim!! " + fi +done + +# For the awk script, D is an array of macro values keyed by name, +# likewise P contains macro parameters if any. Preserve backslash +# newline sequences. + +ac_word_re=[_$as_cr_Letters][_$as_cr_alnum]* +sed -n ' +s/.\{148\}/&'"$ac_delim"'/g +t rset +:rset +s/^[ ]*#[ ]*define[ ][ ]*/ / +t def +d +:def +s/\\$// +t bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3"/p +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2"/p +d +:bsnl +s/["\\]/\\&/g +s/^ \('"$ac_word_re"'\)\(([^()]*)\)[ ]*\(.*\)/P["\1"]="\2"\ +D["\1"]=" \3\\\\\\n"\\/p +t cont +s/^ \('"$ac_word_re"'\)[ ]*\(.*\)/D["\1"]=" \2\\\\\\n"\\/p +t cont +d +:cont +n +s/.\{148\}/&'"$ac_delim"'/g +t clear +:clear +s/\\$// +t bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/"/p +d +:bsnlc +s/["\\]/\\&/g; s/^/"/; s/$/\\\\\\n"\\/p +b cont +' >$CONFIG_STATUS || ac_write_fail=1 + +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + for (key in D) D_is_set[key] = 1 + FS = "" +} +/^[\t ]*#[\t ]*(define|undef)[\t ]+$ac_word_re([\t (]|\$)/ { + line = \$ 0 + split(line, arg, " ") + if (arg[1] == "#") { + defundef = arg[2] + mac1 = arg[3] + } else { + defundef = substr(arg[1], 2) + mac1 = arg[2] + } + split(mac1, mac2, "(") #) + macro = mac2[1] + prefix = substr(line, 1, index(line, defundef) - 1) + if (D_is_set[macro]) { + # Preserve the white space surrounding the "#". + print prefix "define", macro P[macro] D[macro] + next + } else { + # Replace #undef with comments. This is necessary, for example, + # in the case of _POSIX_SOURCE, which is predefined and required + # on some systems where configure will not decide to define it. + if (defundef == "undef") { + print "/*", prefix defundef, macro, "*/" + next + } + } +} +{ print } +_ACAWK +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 + as_fn_error $? "could not setup config headers machinery" "$LINENO" 5 +fi # test -n "$CONFIG_HEADERS" + + +eval set X " :F $CONFIG_FILES :H $CONFIG_HEADERS " +shift +for ac_tag +do + case $ac_tag in + :[FHLC]) ac_mode=$ac_tag; continue;; + esac + case $ac_mode$ac_tag in + :[FHL]*:*);; + :L* | :C*:*) as_fn_error $? "invalid tag \`$ac_tag'" "$LINENO" 5;; + :[FH]-) ac_tag=-:-;; + :[FH]*) ac_tag=$ac_tag:$ac_tag.in;; + esac + ac_save_IFS=$IFS + IFS=: + set x $ac_tag + IFS=$ac_save_IFS + shift + ac_file=$1 + shift + + case $ac_mode in + :L) ac_source=$1;; + :[FH]) + ac_file_inputs= + for ac_f + do + case $ac_f in + -) ac_f="$ac_tmp/stdin";; + *) # Look for the file first in the build tree, then in the source tree + # (if the path is not absolute). The absolute path cannot be DOS-style, + # because $ac_f cannot contain `:'. + test -f "$ac_f" || + case $ac_f in + [\\/$]*) false;; + *) test -f "$srcdir/$ac_f" && ac_f="$srcdir/$ac_f";; + esac || + as_fn_error 1 "cannot find input file: \`$ac_f'" "$LINENO" 5;; + esac + case $ac_f in *\'*) ac_f=`printf "%s\n" "$ac_f" | sed "s/'/'\\\\\\\\''/g"`;; esac + as_fn_append ac_file_inputs " '$ac_f'" + done + + # Let's still pretend it is `configure' which instantiates (i.e., don't + # use $as_me), people would be surprised to read: + # /* config.h. Generated by config.status. */ + configure_input='Generated from '` + printf "%s\n" "$*" | sed 's|^[^:]*/||;s|:[^:]*/|, |g' + `' by configure.' + if test x"$ac_file" != x-; then + configure_input="$ac_file. $configure_input" + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: creating $ac_file" >&5 +printf "%s\n" "$as_me: creating $ac_file" >&6;} + fi + # Neutralize special characters interpreted by sed in replacement strings. + case $configure_input in #( + *\&* | *\|* | *\\* ) + ac_sed_conf_input=`printf "%s\n" "$configure_input" | + sed 's/[\\\\&|]/\\\\&/g'`;; #( + *) ac_sed_conf_input=$configure_input;; + esac + + case $ac_tag in + *:-:* | *:-) cat >"$ac_tmp/stdin" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 ;; + esac + ;; + esac + + ac_dir=`$as_dirname -- "$ac_file" || +$as_expr X"$ac_file" : 'X\(.*[^/]\)//*[^/][^/]*/*$' \| \ + X"$ac_file" : 'X\(//\)[^/]' \| \ + X"$ac_file" : 'X\(//\)$' \| \ + X"$ac_file" : 'X\(/\)' \| . 2>/dev/null || +printf "%s\n" X"$ac_file" | + sed '/^X\(.*[^/]\)\/\/*[^/][^/]*\/*$/{ + s//\1/ + q + } + /^X\(\/\/\)[^/].*/{ + s//\1/ + q + } + /^X\(\/\/\)$/{ + s//\1/ + q + } + /^X\(\/\).*/{ + s//\1/ + q + } + s/.*/./; q'` + as_dir="$ac_dir"; as_fn_mkdir_p + ac_builddir=. + +case "$ac_dir" in +.) ac_dir_suffix= ac_top_builddir_sub=. ac_top_build_prefix= ;; +*) + ac_dir_suffix=/`printf "%s\n" "$ac_dir" | sed 's|^\.[\\/]||'` + # A ".." for each directory in $ac_dir_suffix. + ac_top_builddir_sub=`printf "%s\n" "$ac_dir_suffix" | sed 's|/[^\\/]*|/..|g;s|/||'` + case $ac_top_builddir_sub in + "") ac_top_builddir_sub=. ac_top_build_prefix= ;; + *) ac_top_build_prefix=$ac_top_builddir_sub/ ;; + esac ;; +esac +ac_abs_top_builddir=$ac_pwd +ac_abs_builddir=$ac_pwd$ac_dir_suffix +# for backward compatibility: +ac_top_builddir=$ac_top_build_prefix + +case $srcdir in + .) # We are building in place. + ac_srcdir=. + ac_top_srcdir=$ac_top_builddir_sub + ac_abs_top_srcdir=$ac_pwd ;; + [\\/]* | ?:[\\/]* ) # Absolute name. + ac_srcdir=$srcdir$ac_dir_suffix; + ac_top_srcdir=$srcdir + ac_abs_top_srcdir=$srcdir ;; + *) # Relative name. + ac_srcdir=$ac_top_build_prefix$srcdir$ac_dir_suffix + ac_top_srcdir=$ac_top_build_prefix$srcdir + ac_abs_top_srcdir=$ac_pwd/$srcdir ;; +esac +ac_abs_srcdir=$ac_abs_top_srcdir$ac_dir_suffix + + + case $ac_mode in + :F) + # + # CONFIG_FILE + # + +_ACEOF + +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +# If the template does not know about datarootdir, expand it. +# FIXME: This hack should be removed a few years after 2.60. +ac_datarootdir_hack=; ac_datarootdir_seen= +ac_sed_dataroot=' +/datarootdir/ { + p + q +} +/@datadir@/p +/@docdir@/p +/@infodir@/p +/@localedir@/p +/@mandir@/p' +case `eval "sed -n \"\$ac_sed_dataroot\" $ac_file_inputs"` in +*datarootdir*) ac_datarootdir_seen=yes;; +*@datadir@*|*@docdir@*|*@infodir@*|*@localedir@*|*@mandir@*) + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&5 +printf "%s\n" "$as_me: WARNING: $ac_file_inputs seems to ignore the --datarootdir setting" >&2;} +_ACEOF +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 + ac_datarootdir_hack=' + s&@datadir@&$datadir&g + s&@docdir@&$docdir&g + s&@infodir@&$infodir&g + s&@localedir@&$localedir&g + s&@mandir@&$mandir&g + s&\\\${datarootdir}&$datarootdir&g' ;; +esac +_ACEOF + +# Neutralize VPATH when `$srcdir' = `.'. +# Shell code in configure.ac might set extrasub. +# FIXME: do we really want to maintain this feature? +cat >>$CONFIG_STATUS <<_ACEOF || ac_write_fail=1 +ac_sed_extra="$ac_vpsub +$extrasub +_ACEOF +cat >>$CONFIG_STATUS <<\_ACEOF || ac_write_fail=1 +:t +/@[a-zA-Z_][a-zA-Z_0-9]*@/!b +s|@configure_input@|$ac_sed_conf_input|;t t +s&@top_builddir@&$ac_top_builddir_sub&;t t +s&@top_build_prefix@&$ac_top_build_prefix&;t t +s&@srcdir@&$ac_srcdir&;t t +s&@abs_srcdir@&$ac_abs_srcdir&;t t +s&@top_srcdir@&$ac_top_srcdir&;t t +s&@abs_top_srcdir@&$ac_abs_top_srcdir&;t t +s&@builddir@&$ac_builddir&;t t +s&@abs_builddir@&$ac_abs_builddir&;t t +s&@abs_top_builddir@&$ac_abs_top_builddir&;t t +$ac_datarootdir_hack +" +eval sed \"\$ac_sed_extra\" "$ac_file_inputs" | $AWK -f "$ac_tmp/subs.awk" \ + >$ac_tmp/out || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + +test -z "$ac_datarootdir_hack$ac_datarootdir_seen" && + { ac_out=`sed -n '/\${datarootdir}/p' "$ac_tmp/out"`; test -n "$ac_out"; } && + { ac_out=`sed -n '/^[ ]*datarootdir[ ]*:*=/p' \ + "$ac_tmp/out"`; test -z "$ac_out"; } && + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&5 +printf "%s\n" "$as_me: WARNING: $ac_file contains a reference to the variable \`datarootdir' +which seems to be undefined. Please make sure it is defined" >&2;} + + rm -f "$ac_tmp/stdin" + case $ac_file in + -) cat "$ac_tmp/out" && rm -f "$ac_tmp/out";; + *) rm -f "$ac_file" && mv "$ac_tmp/out" "$ac_file";; + esac \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + ;; + :H) + # + # CONFIG_HEADER + # + if test x"$ac_file" != x-; then + { + printf "%s\n" "/* $configure_input */" >&1 \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" + } >"$ac_tmp/config.h" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + if diff "$ac_file" "$ac_tmp/config.h" >/dev/null 2>&1; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: $ac_file is unchanged" >&5 +printf "%s\n" "$as_me: $ac_file is unchanged" >&6;} + else + rm -f "$ac_file" + mv "$ac_tmp/config.h" "$ac_file" \ + || as_fn_error $? "could not create $ac_file" "$LINENO" 5 + fi + else + printf "%s\n" "/* $configure_input */" >&1 \ + && eval '$AWK -f "$ac_tmp/defines.awk"' "$ac_file_inputs" \ + || as_fn_error $? "could not create -" "$LINENO" 5 + fi + ;; + + + esac + +done # for ac_tag + + +as_fn_exit 0 +_ACEOF +ac_clean_files=$ac_clean_files_save + +test $ac_write_fail = 0 || + as_fn_error $? "write failure creating $CONFIG_STATUS" "$LINENO" 5 + + +# configure is writing to config.log, and then calls config.status. +# config.status does its own redirection, appending to config.log. +# Unfortunately, on DOS this fails, as config.log is still kept open +# by configure, so config.status won't be able to write to it; its +# output is simply discarded. So we exec the FD to /dev/null, +# effectively closing config.log, so it can be properly (re)opened and +# appended to by config.status. When coming back to configure, we +# need to make the FD available again. +if test "$no_create" != yes; then + ac_cs_success=: + ac_config_status_args= + test "$silent" = yes && + ac_config_status_args="$ac_config_status_args --quiet" + exec 5>/dev/null + $SHELL $CONFIG_STATUS $ac_config_status_args || ac_cs_success=false + exec 5>>config.log + # Use ||, not &&, to avoid exiting from the if with $? = 1, which + # would make configure fail if this is the last instruction. + $ac_cs_success || as_fn_exit 1 +fi +if test -n "$ac_unrecognized_opts" && test "$enable_option_checking" != no; then + { printf "%s\n" "$as_me:${as_lineno-$LINENO}: WARNING: unrecognized options: $ac_unrecognized_opts" >&5 +printf "%s\n" "$as_me: WARNING: unrecognized options: $ac_unrecognized_opts" >&2;} +fi + + diff --git a/src/main/c/yarp/configure.ac b/src/main/c/yarp/configure.ac deleted file mode 100644 index 79e4405fd65e..000000000000 --- a/src/main/c/yarp/configure.ac +++ /dev/null @@ -1,22 +0,0 @@ -m4_define([_YP_VERSION_MAJOR], [0]) -m4_define([_YP_VERSION_MINOR], [4]) -m4_define([_YP_VERSION_PATCH], [0]) -m4_define([_YP_VERSION], [_YP_VERSION_MAJOR._YP_VERSION_MINOR._YP_VERSION_PATCH]) - -AC_INIT([YARP],[_YP_VERSION],[https://github.com/ruby/yarp/issues/new],[yarp],[https://github.com/ruby/yarp]) - -AC_PROG_CC -AC_C_INLINE -AC_DEFINE([_XOPEN_SOURCE], [700], [_XOPEN_SOURCE]) -AC_DEFINE([YP_VERSION_MAJOR], [_YP_VERSION_MAJOR], [YP_VERSION_MAJOR]) -AC_DEFINE([YP_VERSION_MINOR], [_YP_VERSION_MINOR], [YP_VERSION_MINOR]) -AC_DEFINE([YP_VERSION_PATCH], [_YP_VERSION_PATCH], [YP_VERSION_PATCH]) -AC_DEFINE([YP_VERSION], ["_YP_VERSION"], [YP_VERSION]) - -AC_CHECK_FUNCS([mmap]) -AC_CHECK_FUNCS([snprintf]) -AC_CHECK_FUNCS([strncasecmp]) - -AC_CONFIG_HEADERS([include/yarp/config.h:config.h.in]) -AC_CONFIG_FILES([Makefile]) -AC_OUTPUT diff --git a/src/main/c/yarp/include/yarp/defines.h b/src/main/c/yarp/include/yarp/defines.h index 19a71b02faa0..10a083185916 100644 --- a/src/main/c/yarp/include/yarp/defines.h +++ b/src/main/c/yarp/include/yarp/defines.h @@ -33,6 +33,11 @@ # define YP_ATTRIBUTE_UNUSED #endif +// inline +#if defined(_MSC_VER) && !defined(inline) +# define inline __inline +#endif + // strncasecmp #if !defined(HAVE_STRNCASECMP) && !defined(strncasecmp) // In case strncasecmp isn't present on the system, we provide our own. From 152fface17f159469791e96ffd678241dce8d7e5 Mon Sep 17 00:00:00 2001 From: Benoit Daloze Date: Mon, 3 Jul 2023 09:43:40 +0200 Subject: [PATCH 10/10] Handle clean on YARPNativeBuildTask before the Makefile exists --- mx.truffleruby/mx_truffleruby.py | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/mx.truffleruby/mx_truffleruby.py b/mx.truffleruby/mx_truffleruby.py index 37b4d2debdf8..235494dc6762 100644 --- a/mx.truffleruby/mx_truffleruby.py +++ b/mx.truffleruby/mx_truffleruby.py @@ -138,6 +138,12 @@ def build(self): mx.run(['./configure'], cwd=self.subject.dir) super(YARPNativeBuildTask, self).build() # make + def clean(self, forBuild=False): + if exists(join(self.subject.dir, 'Makefile')): + super(YARPNativeBuildTask, self).clean(forBuild=forBuild) + else: + pass + # Commands def jt(*args):