From cb95198e0b426160d56534be8b1a356f6fbcdac0 Mon Sep 17 00:00:00 2001 From: christian-byrne Date: Tue, 8 Oct 2024 03:32:17 -0700 Subject: [PATCH] Add AST tools/visualizers and polymorphism examples --- ast_visualizers/color_code_tokens.py | 186 +++ ast_visualizers/sentence_visualization.png | Bin 0 -> 39720 bytes polymorphism/Foo.java | 26 + polymorphism/FooChild.java | 13 + polymorphism/FooTest.java | 22 + polymorphism/Polymorphism1.java | 34 + polymorphism/Polymorphism2.java | 103 ++ polymorphism/Polymorphism3.java | 42 + src/quiz03/quiz03.sml | 17 + src/sml_practice/ica6.sml | 24 + tree-sitters/SML-tree-sitter/grammar.js | 766 ++++++++++++ tree-sitters/prolog-tree-sitter/README.md | 1 + tree-sitters/prolog-tree-sitter/grammar.js | 460 +++++++ tree-sitters/ruby-tree-sitter/README.md | 1 + tree-sitters/ruby-tree-sitter/grammar.js | 1286 ++++++++++++++++++++ 15 files changed, 2981 insertions(+) create mode 100644 ast_visualizers/color_code_tokens.py create mode 100644 ast_visualizers/sentence_visualization.png create mode 100644 polymorphism/Foo.java create mode 100644 polymorphism/FooChild.java create mode 100644 polymorphism/FooTest.java create mode 100644 polymorphism/Polymorphism1.java create mode 100644 polymorphism/Polymorphism2.java create mode 100644 polymorphism/Polymorphism3.java create mode 100644 src/quiz03/quiz03.sml create mode 100644 src/sml_practice/ica6.sml create mode 100644 tree-sitters/SML-tree-sitter/grammar.js create mode 100644 tree-sitters/prolog-tree-sitter/README.md create mode 100644 tree-sitters/prolog-tree-sitter/grammar.js create mode 100644 tree-sitters/ruby-tree-sitter/README.md create mode 100644 tree-sitters/ruby-tree-sitter/grammar.js diff --git a/ast_visualizers/color_code_tokens.py b/ast_visualizers/color_code_tokens.py new file mode 100644 index 0000000..633ba53 --- /dev/null +++ b/ast_visualizers/color_code_tokens.py @@ -0,0 +1,186 @@ +from PIL import Image, ImageDraw, ImageFont +import nltk +import os + +SAVE_PATH = "/home/c_byrne/school/courses/csc372-programming_languages/assignments/small-assignments" +FILENAME = "color_coded_tokens.png" +DISPLAY_EXAMPLES = True +REPLACE_TOKEN_TYPE_NAMES = { + "DT: determiner": "AT: article", + "IN: preposition or conjunction, subordinating": "IN: preposition", +} + +if not os.path.exists(SAVE_PATH): + raise FileExistsError(f"Path {SAVE_PATH} does not exist") + +# Download necessary NLTK datasets +nltk.download("punkt") +nltk.download("averaged_perceptron_tagger") + +# Sample sentence +sentence = "the girl has a gift for the boy in the car" + +# Tokenize and POS tag the sentence +tokens = nltk.word_tokenize(sentence) +pos_tags = nltk.pos_tag(tokens) +# Define color map for POS tags +color_map = { + "NN": "blue", # Noun + "VB": "green", # Verb + "JJ": "orange", # Adjective + "RB": "purple", # Adverb + "DT": "red", # Determiner + "IN": "brown", # Preposition + "CC": "pink", # Coordinating conjunction + "PR": "gray", # Pronoun + "MD": "olive", # Modal + "TO": "cyan", # To + "UH": "gold", # Interjection + "FW": "magenta", # Foreign word + "CD": "teal", # Cardinal number + "WP": "navy", # Wh-pronoun + "WR": "lime", # Wh-adverb + "EX": "maroon", # Existential there + "RP": "indigo", # Particle + "POS": "khaki", # Possessive ending + "SYM": "sienna", # Symbol + "LS": "orchid", # List item marker + "PDT": "salmon", # Predeterminer + "WP$": "lavender", # Possessive wh-pronoun + "NNP": "skyblue", # Proper noun + "NNPS": "lightcoral", # Proper noun plural + "NNS": "lightgreen", # Noun plural + "VBG": "lightyellow", # Verb gerund + "VBD": "lightcyan", # Verb past tense +} + +BASE_FONTSIZE = 64 + +# Create a blank image with white background +image_width = 2048 +image_height = image_width // 2 +image = Image.new("RGB", (image_width, image_height), (255, 255, 255)) +draw = ImageDraw.Draw(image) + +# Load a font (use a default one, or provide a path to a specific font) +font = ImageFont.truetype("arial.ttf", BASE_FONTSIZE) + +# Starting coordinates for drawing the text +OFFSET_X = image_width // 13 +OFFSET_Y = image_height // 6 +x, y = OFFSET_X, image_height // 2 - OFFSET_Y + +seen_token_types = set() + +# Draw each word with color-coded based on its POS tag +for word, tag in pos_tags: + seen_token_types.add(tag) + tag_base = tag[:2] + color = color_map.get(tag_base, (0, 0, 0)) # Default to black if tag not found + + # Draw the word + draw.text((x, y), word, fill=color, font=font) + + # Get the bounding box of the text + bbox = draw.textbbox((x, y), word, font=font) + text_width = bbox[2] - bbox[0] + + # Update x-coordinate for the next word (add a little space) + x += text_width + 10 + +nltk.download("wordnet") +nltk.download("tagsets") + +from nltk.corpus import wordnet +from nltk.data import load +import re +from textwrap import wrap + + +def format_descriptions(tags, tagdict, tag_base, include_examples=True, example_count=5): + for tag in tags: + entry = tagdict[tag] + # defn = [tag + ": " + entry[0]] + # examples = wrap( + # entry[1], width=75, initial_indent=" ", subsequent_indent=" " + # ) + tag_display = tag if tag == tag_base else f"{tag} ({tag_base})" + descrip = f"{tag_display}: {entry[0]}" + # descrip = f"{entry[0]}" + if include_examples: + descrip += f" ({', '.join(entry[1].split(' ')[:example_count])}, ...)" + return apply_replacements(descrip) + + +def apply_replacements(description: str) -> str: + for old, new in REPLACE_TOKEN_TYPE_NAMES.items(): + description = description.replace(old, new) + return description + + +def get_tag_description( + tagpattern, tag_base, tagset="upenn_tagset", include_examples=True, example_count=5 +): + # try: + # # return wordnet._tagset. + # return wordnet._tagset + # except KeyError: + # print(f"Unknown tag: {tag}") + # nltk.help.upenn_tagset(tag) + # return tag + + tagdict = load("help/tagsets/" + tagset + ".pickle") + if tagdict is None: + raise ValueError(f"Unknown tagset: {tagset}") + if not tagpattern: + return format_descriptions( + sorted(tagdict), + tagdict, + tag_base, + include_examples=include_examples, + example_count=example_count, + ) + elif tagpattern in tagdict: + return format_descriptions( + [tagpattern], + tagdict, + tag_base, + include_examples=include_examples, + example_count=example_count, + ) + else: + tagpattern = re.compile(tagpattern) + tags = [tag for tag in sorted(tagdict) if tagpattern.match(tag)] + if tags: + return format_descriptions( + tags, + tagdict, + tag_base, + include_examples=include_examples, + example_count=example_count, + ) + else: + print("No matching tags found.") + + +# Draw the legend +legend_x = OFFSET_X // 4 +legend_y = image_height // 2 + OFFSET_Y // 6 + +font = ImageFont.truetype("arial.ttf", BASE_FONTSIZE // 4) +for i, tag in enumerate(seen_token_types): + tag_base = tag[:2] + color = color_map.get(tag_base, (0, 0, 0)) # Default to black if tag not found + + display_text = get_tag_description(tag, tag_base, include_examples=DISPLAY_EXAMPLES) + draw.text((legend_x, legend_y), display_text, fill=color, font=font) + legend_y += OFFSET_Y // 4 + +# Save or display the image +image.show() + +# If file exists, overwrite it +if os.path.exists(os.path.join(SAVE_PATH, FILENAME)): + os.remove(os.path.join(SAVE_PATH, FILENAME)) + +image.save(os.path.join(SAVE_PATH, FILENAME)) diff --git a/ast_visualizers/sentence_visualization.png b/ast_visualizers/sentence_visualization.png new file mode 100644 index 0000000000000000000000000000000000000000..905b7a73a3088462d7330a3baaf218f77aba3757 GIT binary patch literal 39720 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%0Dw{kA1iN|Hx{ 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 literal 0 HcmV?d00001 diff --git a/polymorphism/Foo.java b/polymorphism/Foo.java new file mode 100644 index 0000000..2f0d31d --- /dev/null +++ b/polymorphism/Foo.java @@ -0,0 +1,26 @@ +public class Foo { + private int x; + private int y; + + public Foo(int x, int y) { + this.x = x; + this.y = y; + } + + public int add() { + return x + y; + } + + public static void main(String[] args) { + Foo one = new Foo(1, 1); + Foo two = new Foo(2, 2); + System.out.println(one.equals(two)); + System.out.println(one.hashCode()); + System.out.println(one.toString()); + /* Note that this class can use "equals", + * "hashCode", and "toString" because + * all classes in Java inherits these + * methods from the Object class. + */ + } +} diff --git a/polymorphism/FooChild.java b/polymorphism/FooChild.java new file mode 100644 index 0000000..490bd99 --- /dev/null +++ b/polymorphism/FooChild.java @@ -0,0 +1,13 @@ +public class FooChild extends Foo { + public FooChild(int x, int y) { + super(x, y); + } + + public static void main(String[] args) { + FooChild one = new FooChild(3, 4); + System.out.println(one.add()); + /* This statement works because FooChild + * inherits the "add" method from its parent + * class, Foo. */ + } +} diff --git a/polymorphism/FooTest.java b/polymorphism/FooTest.java new file mode 100644 index 0000000..ec7b0f0 --- /dev/null +++ b/polymorphism/FooTest.java @@ -0,0 +1,22 @@ +public class FooTest { + public static boolean equals(Foo x, Foo y) { + return x.add() == y.add(); + } + + public static void main(String[] args) { + Foo x = new Foo(2, 2); + FooChild y = new FooChild(7, 7); + System.out.println(FooTest.equals(x, x)); + System.out.println(FooTest.equals(x, new Foo(3, 3))); + System.out.println(FooTest.equals(y, new Foo(11, 3))); + System.out.println(FooTest.equals(x, y)); + System.out.println(FooTest.equals(y, x)); + } +} + +/* This is where we see subtype polymorphism. + * The equals method is defined to work for items of + * type Foo. But the tests below show that it + * works on items of type Foo and items of type FooChild. + */ + diff --git a/polymorphism/Polymorphism1.java b/polymorphism/Polymorphism1.java new file mode 100644 index 0000000..1ff6d01 --- /dev/null +++ b/polymorphism/Polymorphism1.java @@ -0,0 +1,34 @@ +public class Polymorphism1 { + public static int pow (int a, int b) {System.out.println("first"); + if (b <= 0) + return 1; + return a*Polymorphism1.pow(a, b-1); + } + + public static double pow (double a, int b) {System.out.println("second"); + if (b <= 0) + return 1.0; + return a*Polymorphism1.pow(a, b-1); + } + + /* public static int pow (double a, int b) {System.out.println("third"); + if (b <= 0) + return 1; + return ((int) a)*Polymorphism1.pow(a, b-1); + }*/ + + public static double pow(int a, double b) {System.out.println("fourth"); + if(b < 1) + return 1; + return a*Polymorphism1.pow(a, b-1); + } + + public static void main(String[] args) { + System.out.println(Polymorphism1.pow(2, 3)); + System.out.println(Polymorphism1.pow(2.0, 3)); + System.out.println(Polymorphism1.pow(2, 3.0)); + } +} + +/* In Java, method overloading is allowed. This means that a programmer can write two methods that have the same name but take different parameters. Notice that the first definition above takes two integers, the second takes a double and an int, and the fourth takes an int and a double. Java knows which definition to use based on the types of the arguments that get passed in when the method is called. For method overloading to work in Java, it is necessary that the parameters are different in number, type, and/or order. The third definition is commented out because this is not legal due to the fact that the parameters match those of the second definition in type, number, and order. The return types of the two methods are different, but that is not enough for Java to determine which method should be used. Some languages do allow overloading of methods that only differ in return type, but most do not. This is because it is much easier for a compiler to determine which method to use based on the types of the arguments than on the type that is returned. */ + diff --git a/polymorphism/Polymorphism2.java b/polymorphism/Polymorphism2.java new file mode 100644 index 0000000..cc82b1c --- /dev/null +++ b/polymorphism/Polymorphism2.java @@ -0,0 +1,103 @@ +public class Polymorphism2 { + public static void main(String[] args) { + //promotion + double x = 2*8.2; + /* In the statement above, * works on the two numbers because of + * a type of implicit type conversion called promotion. In this kind + * of coercion, a value of a smaller type is automatically converted to a larger + * type (in this case int to double), which then allows the multiplication + * operator to be applied. The result will be a double as well. Note that + * not all languages will do this. SML, for example, will not. + */ + + //function call--polymorphic + System.out.println(Polymorphism2.f((byte) 2)); + System.out.println(Polymorphism2.f((short) 2)); + System.out.println(Polymorphism2.f('a')); + System.out.println(Polymorphism2.f(3)); + System.out.println(Polymorphism2.f(4L)); + System.out.println(Polymorphism2.f(5.6F)); + + /* All of the statements above are legal in Java because in each + * case, the value that is passed in is of a type that is equal or + * smaller than a double, which is the type of f's parameter. + * This is more implicit type coercion, which is a form of polymorphism. + * Note that we are dealing with primitive data here, not objects. + * But what is happening is that the method is expecting a double, + * and all of these values can be automatically converted to a double + * because it would not result in loss of data. + */ + + //what happens here? + int y = (int)2.0; + /* In this case, we are trying to put a double into a space reserved + * for an integer. This will not be done automatically in Java because + * doubles are larger than integers, so there would be potential data loss. + * Here, the programmer must specify that they want the conversion to take + * place with explicit casting. + */ + + //function call--polymorphic + System.out.println(Polymorphism2.g((byte) 1)); + System.out.println(Polymorphism2.g((short) 2)); + System.out.println(Polymorphism2.g('a')); + //System.out.println(Polymorphism2.g(3.0)); + //System.out.println(Polymorphism2.g(4L)); + //System.out.println(Polymorphism2.g(5.6F)); + + /* The parameter expected by method g above is an integer. + * So the first three statements work because bytes, shorts, + * and chars are all small enough to fit into an integer spot. + * This is why the implicit type coercion can happen. + * But in the last three cases, the statements would produce errors + * because doubles, longs, and floats are too big to fit into + * an integer spot. Therefore, the implicit type conversion cannot + * be done. Explicit casting would be necessary to make these work. + */ + + + Polymorphism2.h('a', 5); + /* Here, there are two options for h that + * would work with these arguments because chars can be + * converted to integers. However, it will use the + * method that is closest to the arguments passed in. In other + * words, Java will not do the implicit conversion unless it + * is necessary. */ + + //Polymorphism2.h('a','b'); + /* This line is commented out because it will not compile, + * which may seem odd since chars can be converted to integers + * so any one of the h definitions could work with these arguments. + * We know that Java will do as little conversion as necessary, so + * it will not choose h1 over the other two. The problem is that + * h2 and h3 require the exact same amount of conversion, so + * Java does not know what to do--so it just doesn't allow it. + */ + } + + public static double f (double x) {System.out.println("f1"); + return x; + } + + public static int g (int x) { + return x; + } + + //Where things get tricky... + + /*public static int f(int x) {System.out.println("f2"); + return x*x; + }*/ + + public static void h(int x, int y) { + System.out.println("h1"); + } + + public static void h(char x, int y) { + System.out.println("h2"); + } + + public static void h(int x, char y) { + System.out.println("h3"); + } +} diff --git a/polymorphism/Polymorphism3.java b/polymorphism/Polymorphism3.java new file mode 100644 index 0000000..573ff56 --- /dev/null +++ b/polymorphism/Polymorphism3.java @@ -0,0 +1,42 @@ +public class Polymorphism3 { + public static void main(String[] args) { + int a = 1; + int b = 2; + double x = 1.1; + double y = 2.2; + String s = "hi"; + String t = "bye"; + + System.out.println(a + b + s);//3hi + System.out.println(s + a + b);//hi12 + System.out.println(s + (a + b));//hi3 + System.out.println(a + x);//2.1 + System.out.println(x + y);//3.3 + System.out.println(a + s);//1hi + System.out.println(s + y);//hi2.2 + System.out.println(s + t);//hibye + } +} + +/*** + * First, let's note that println is overloaded to handle the various primitive data types as + * well as Strings. + * Second, the "+" operator is overloaded. When the arguments are integers, it adds them + * together. When the arguments are doubles, it adds them together. When the arguments + * are Strings, it concatenates them. + * Finally, there is a lot of implicit type coercion happening. + * In the first print + * statement, the a and b are added together and then converted to a String that is + * concatenated with s. + * In the second print statement, a is converted to a String and + * concatenated with s. Then b is converted to a String and concatenated with the "hi1". + * This is because + is left-associative and will be applied from left to right unless + * parentheses are used to override the associativity, + * which is what happens in the + * third print statement. There, the addition is done first because of the parenthesis, then + * the type coercion and concatenation. + * In the fourth print statement, the integer is converted to a double before adding. + * In the fifth print statement, no coercion is needed as they are both doubles. + * In the sixth print statement, the double is converted to a String before concatenation. + * In the seventh print statement, no coercion is needed as they are both Strings. */ + diff --git a/src/quiz03/quiz03.sml b/src/quiz03/quiz03.sml new file mode 100644 index 0000000..1c482d8 --- /dev/null +++ b/src/quiz03/quiz03.sml @@ -0,0 +1,17 @@ + +(* -Question 4- *) + +fun countZeros([]) = 0 + | countZeros(0::xs) = 1 + countZeros(xs) + | countZeros(_::xs) = countZeros(xs) + + +val result = countZeros([1,2,0,0,2,0,1,0]); (* 4 *) + + +(* -Question 6- *) + +fun foo(_, []) = [] + | foo(e, x::xs) = if e = x then foo(e, xs) else x::foo(e, xs); + +val result2 = foo(3, [1,2,3,1,2,3]); (* [1,2,1,2] *) diff --git a/src/sml_practice/ica6.sml b/src/sml_practice/ica6.sml new file mode 100644 index 0000000..67c9550 --- /dev/null +++ b/src/sml_practice/ica6.sml @@ -0,0 +1,24 @@ + +(* halve: 'a list -> 'a list * a' list) *) + +fun halve([]) = ([], []) + | halve([x]) = ([x], []) + | halve(x::y::li) = + let + val (a, b) = halve(li) + in + (x::a, y::b) + end; + +val halved = [1,2,3,4,5,6]; + +(* merge: 'a list * 'a list -> 'a list *) +(* precondition: the input lists are sorted *) +(* postcondition: returned lists should be sorted *) + +fun merge([], []) = [] + | merge([], x::li2) = x::merge([], li2) + | merge(x::li1, []) = x:: merge(li1, []) + | merge(x::li1, y::li2) = x::y::merge(li1, li2); + +val merged = merge([1,2,3], [4,5,6]); diff --git a/tree-sitters/SML-tree-sitter/grammar.js b/tree-sitters/SML-tree-sitter/grammar.js new file mode 100644 index 0000000..8b79d89 --- /dev/null +++ b/tree-sitters/SML-tree-sitter/grammar.js @@ -0,0 +1,766 @@ +const INT = token(/~?\d+|~?0x[0-9A-Fa-f]+/); +const WORD = token(/0w\d+|0wx[0-9A-Fa-f]+/); +const FLOAT = token(/~?\d+\.\d+([Ee]~?\d+)?/); + +module.exports = grammar({ + name: 'standard_ml', + + extras: $ => [ + /\s/, + $.comment, + ], + + word: $ => $.ident, + + externals: $ => [ + $.comment, + $.string, + $.character, + ], + + rules: { + source_file: $ => seq( + choice($._exp, repeat($._sdec)), + repeat( + seq( + ';', + choice($._exp, repeat($._sdec)), + ), + ), + ), + + // Top-level declarations + + _sdec: $ => choice( + $.structure_dec, + $.signature_dec, + $.funsig_dec, + $.functor_dec, + $.local_dec, + $._ldec, + ), + + // Structures + + structure_dec: $ => seq( + 'structure', + $.strb, + repeat(seq('and', $.strb)), + ), + + strb: $ => seq( + $.ident, + optional($._sigconstraint_op), + '=', + $._str, + ), + + _sigconstraint_op: $ => choice( + $.transparent_sigconstraint_op, + $.opaque_sigconstraint_op, + ), + + transparent_sigconstraint_op: $ => seq(':', $._sign), + + opaque_sigconstraint_op: $ => seq(':>', $._sign), + + _str: $ => choice( + $.var_struct, + $.base_struct, + $.app_struct, + $.let_struct, + $.constraint_struct, + ), + + var_struct: $ => $.qident, + + base_struct: $ => seq( + 'struct', + repeat(choice(';', $._strdec)), + 'end', + ), + + _strdec: $ => choice( + $.structure_dec_strdec, + $.functor_dec_strdec, + $.local_dec_strdec, + $._ldec, + ), + + structure_dec_strdec: $ => seq( + 'structure', + $.strb, + repeat(seq('and', $.strb)), + ), + + functor_dec_strdec: $ => seq( + 'functor', + $.fctb, + repeat(seq('and', $.fctb)), + ), + + local_dec_strdec: $ => seq( + 'local', + repeat(choice(';', $._strdec)), + 'in', + repeat(choice(';', $._strdec)), + 'end', + ), + + app_struct: $ => seq( + $.qident, + repeat1($.arg_fct), + ), + + let_struct: $ => seq( + 'let', + repeat(choice(';', $._strdec)), + 'in', + $._str, + 'end', + ), + + constraint_struct: $ => seq($._str, choice(':', ':>'), $._sign), + + // Signatures + + signature_dec: $ => seq( + 'signature', + $.sigb, + repeat(seq('and', $.sigb)), + ), + + sigb: $ => seq($.ident, '=', $._sign), + + _sign: $ => choice( + $.var_sign, + $.base_sign, + $.aug_sign, + ), + + var_sign: $ => $.ident, + + base_sign: $ => seq('sig', repeat(choice(';', $._spec)), 'end'), + + aug_sign: $ => prec.right(seq($._sign, 'where', $._whspec, repeat(seq('and', $._whspec)))), + + _spec: $ => choice( + $.str_spec, + $.functor_spec, + $.datatype_repl_spec, + $.datatype_spec, + $.type_spec, + $.eqtype_spec, + $.val_spec, + $.exception_spec, + $.sharing_spec, + $.include_spec, + ), + + str_spec: $ => seq( + 'structure', + $.strspec, + repeat(seq('and', $.strspec)), + ), + + strspec: $ => seq( + $.ident, + ':', + $._sign, + optional(seq('=', $.qident)), + ), + + functor_spec: $ => seq( + 'functor', + $.fctspec, + repeat(seq('and', $.fctspec)), + ), + + fctspec: $ => seq($.ident, $._fsig), + + datatype_repl_spec: $ => seq( + 'datatype', + $.dtrepl, + ), + + dtrepl: $ => seq($._full_ident, '=', 'datatype', $.con_ty), + + datatype_spec: $ => seq( + 'datatype', + $.db, + repeat(seq('and', $.db)), + optional(seq('withtype', $.tb, repeat(seq('and', $.tb)))), + ), + + type_spec: $ => seq( + 'type', + $.tyspec, + repeat(seq('and', $.tyspec)), + ), + + tyspec: $ => seq( + optional($.tyvar_seq), + $._full_ident, + optional(seq('=', $._ty)), + ), + + eqtype_spec: $ => seq( + 'eqtype', + $.tyspec, + repeat(seq('and', $.tyspec)), + ), + + val_spec: $ => seq( + 'val', + $.valspec, + repeat(seq('and', $.valspec)), + ), + + valspec: $ => seq( + optional('op'), + $._full_ident, + ':', + $._ty + ), + + exception_spec: $ => seq( + 'exception', + $.exnspec, + repeat(seq('and', $.exnspec)), + ), + + exnspec: $ => seq($._full_ident, optional(seq('of', $._ty))), + + sharing_spec: $ => seq( + 'sharing', + $.sharespec, + repeat(seq('and', $.sharespec)), + ), + + sharespec: $ => seq(optional('type'), $.qident, repeat1(seq('=', $.qident))), + + include_spec: $ => seq( + 'include', + choice( + $._sign, + seq($.ident, repeat1($.ident)), + ), + ), + + _whspec: $ => choice( + $.type_whspec, + $.struct_whsepc, + ), + + type_whspec: $ => seq('type', optional($.tyvar_seq), $.qident, '=', $._ty), + + struct_whsepc: $ => seq($.qident, '=', $.qident), + + // Funsigs + + funsig_dec: $ => seq( + 'funsig', + $.fsigb, + repeat(seq('and', $.fsigb)), + ), + + fsigb: $ => seq($.ident, repeat1($.fparam), '=', $._sign), + + _fsig: $ => choice( + $.var_fsig, + $.base_fsig, + ), + + var_fsig: $ => seq(':', $.ident), + + base_fsig: $ => seq(repeat1($.fparam), ':', $._sign), + + // Functors + + functor_dec: $ => seq( + 'functor', + $.fctb, + repeat(seq('and', $.fctb)), + ), + + fctb: $ => choice( + seq($.ident, repeat1($.fparam), optional($._sigconstraint_op), '=', $._str), + seq($.ident, optional($._fsigconstraint_op), '=', $._fct_exp), + ), + + fparam: $ => choice( + seq('(', $.ident, ':', $._sign, ')'), + seq('(', repeat(choice(';', $._spec)), ')'), + ), + + _fsigconstraint_op: $ => choice( + $.transparent_fsigconstraint_op, + $.opaque_fsigconstraint_op, + ), + + transparent_fsigconstraint_op: $ => seq(':', $.ident), + + opaque_fsigconstraint_op: $ => seq(':>', $.ident), + + _fct_exp: $ => choice( + $.var_fct_exp, + $.app_fct_exp, + $.let_fct_exp, + ), + + var_fct_exp: $ => $.qident, + + app_fct_exp: $ => seq($.qident, repeat1($.arg_fct)), + + arg_fct: $ => choice( + seq('(', repeat(choice(';', $._strdec)), ')'), + seq('(', $._str, ')'), + ), + + let_fct_exp: $ => seq( + 'let', + repeat(choice(';', $._strdec)), + 'in', + $._fct_exp, + 'end', + ), + + // Misc + + local_dec: $ => seq( + 'local', + repeat(choice(';', $._sdec)), + 'in', + repeat(choice(';', $._sdec)), + 'end', + ), + + _ldec: $ => choice( + $.val_ldec, + $.fun_ldec, + $.type_ldec, + $.datatype_repl_ldec, + $.datatype_ldec, + $.abstype_ldec, + $.exception_ldec, + $.open_ldec, + $.fixity_ldec, + $.overload_ldec, + ), + + // Value declarations + + val_ldec: $ => seq( + 'val', + optional($.tyvar_seq), + optional('rec'), + $.vb, + repeat(seq('and', $.vb)), + ), + + vb: $ => seq( + $._pat, + '=', + $._exp, + ), + + // Function declarations + + fun_ldec: $ => seq( + 'fun', + optional($.tyvar_seq), + $.fb, + repeat(seq('and', $.fb)), + ), + + fb: $ => seq($.clause, repeat(seq('|', $.clause))), + + clause: $ => seq( + repeat1($._apat), + optional($.constraint), + '=', + $._exp + ), + + constraint: $ => seq(':', $._ty), + + // Type declarations + + type_ldec: $ => seq( + 'type', + $.tb, + repeat(seq('and', $.tb)), + ), + + tb: $ => seq( + optional($.tyvar_seq), + $._full_ident, + '=', + $._ty, + ), + + // Datatype declarations + + datatype_repl_ldec: $ => seq( + 'datatype', + $.dtrepl, + ), + + datatype_ldec: $ => seq( + 'datatype', + $.db, + repeat(seq('and', $.db)), + optional( + seq( + 'withtype', + $.tb, + repeat(seq('and', $.tb)), + ), + ), + ), + + db: $ => seq( + optional($.tyvar_seq), + $._full_ident, + '=', + $.constr, + repeat(seq('|', $.constr)), + ), + + constr: $ => seq( + optional('op'), + $._full_ident, + optional(seq('of', $._ty)), + ), + + // Abstype declarations + + abstype_ldec: $ => seq( + 'abstype', + $.db, + repeat(seq('and', $.db)), + optional(seq('withtype', $.tb, repeat(seq('and', $.tb)))), + 'with', + repeat(choice(';', $._ldec, $.local_dec_let)), + 'end', + ), + + // Exception declarations + + exception_ldec: $ => seq( + 'exception', + $.eb, + repeat(seq('and', $.eb)), + ), + + eb: $ => seq( + optional('op'), + $._full_ident, + optional(choice($.exn_gen, $.exn_def)), + ), + + exn_gen: $ => seq('of', $._ty), + + exn_def: $ => seq('=', $.qident), + + // Misc declarations + + open_ldec: $ => seq( + 'open', + repeat1($.qident), + ), + + fixity_ldec: $ => seq( + choice(seq(choice('infix', 'infixr'), optional($.int_constant)), 'nonfix'), + repeat1($._full_ident), + ), + + overload_ldec: $ => seq( + 'overload', + $._full_ident, + ':', + $._ty, + 'as', + $._exp, + repeat(seq('and', $._exp)), + ), + + // Patterns + + _pat: $ => choice( + $.as_pat, + $.constraint_pat, + $.app_pat, + ), + + as_pat: $ => prec.right(seq($._pat, 'as', $._pat)), + + constraint_pat: $ => seq($._pat, ':', $._ty), + + app_pat: $ => repeat1($._apat), + + _apat: $ => choice( + $._apat_, + $.paren_pat, + $.var_pat, + $.tuple_unit_pat, + $.tuple_pat, + $.or_pat, + ), + + paren_pat: $ => seq('(', $._pat, ')'), + + var_pat: $ => $._full_ident, + + tuple_unit_pat: $ => seq('(', ')'), + + tuple_pat: $ => seq('(', $._pat, repeat1(seq(',', $._pat)), ')'), + + or_pat: $ => seq('(', $._pat, repeat1(seq('|', $._pat)), ')'), + + _apat_: $ => choice( + $.op_pat, + $.access_pat, + $.constant_pat, + $.wild_pat, + $.list_pat, + $.vector_pat, + $.rec_unit_pat, + $.rec_pat, + ), + + op_pat: $ => seq('op', $._full_ident), + + access_pat: $ => seq( + optional('op'), + $.ident, + '.', + $.qident, + ), + + constant_pat: $ => $._constant, + + wild_pat: $ => '_', + + list_pat: $ => choice( + seq('[', ']'), + seq('[', $._pat, repeat(seq(',', $._pat)), ']'), + ), + + vector_pat: $ => choice( + seq('#[', ']'), + seq('#[', $._pat, repeat(seq(',', $._pat)), ']'), + ), + + rec_unit_pat: $ => seq('{', '}'), + + rec_pat: $ => seq('{', $.plabels, '}'), + + plabels: $ => choice( + seq($.plabel, repeat(seq(',', $.plabel)), optional(seq(',', '...'))), + '...', + ), + + plabel: $ => choice( + seq($.selector, '=', $._pat), + seq( + $._full_ident, + optional(seq(':', $._ty)), + optional(seq('as', $._pat)), + ), + ), + + // Matches + + match: $ => prec.right(seq($.rule, repeat(seq('|', $.rule)))), + + rule: $ => prec.right(seq($._pat, '=>', $._exp)), + + // Expressions + + _exp: $ => choice( + $.handle_exp, + $.orelse_exp, + $.andalso_exp, + $.constraint_exp, + $.app_exp, + $.fn_exp, + $.case_exp, + $.while_exp, + $.if_exp, + $.raise_exp, + ), + + handle_exp: $ => prec.right(seq($._exp, 'handle', $.match)), + + orelse_exp: $ => prec.right(seq($._exp, 'orelse', $._exp)), + + andalso_exp: $ => prec.right(seq($._exp, 'andalso', $._exp)), + + constraint_exp: $ => seq($._exp, ':', $._ty), + + app_exp: $ => repeat1(choice($._aexp, $.var_exp)), + + fn_exp: $ => seq('fn', $.match), + + case_exp: $ => seq('case', $._exp, 'of', $.match), + + while_exp: $ => prec.left(seq('while', $._exp, 'do', $._exp)), + + if_exp: $ => prec.left(seq('if', $._exp, 'then', $._exp, 'else', $._exp)), + + raise_exp: $ => prec.left(seq('raise', $._exp)), + + var_exp: $ => $._full_ident, + + _aexp: $ => choice( + $.op_exp, + $.access_exp, + $.constant_exp, + $.selector_exp, + $.rec_exp, + $.rec_unit_exp, + $.tuple_unit_exp, + $.seq_exp, + $.tuple_exp, + $.list_exp, + $.vector_exp, + $.let_exp, + ), + + op_exp: $ => seq('op', $._full_ident), + + access_exp: $ => seq( + optional('op'), + $.ident, + '.', + $.qident, + ), + + constant_exp: $ => $._constant, + + selector_exp: $ => seq('#', $.selector), + + rec_exp: $ => seq('{', $.elabel, repeat(seq(',', $.elabel)), '}'), + + elabel: $ => seq($.selector, '=', $._exp), + + rec_unit_exp: $ => seq('{', '}'), + + tuple_unit_exp: $ => seq('(', ')'), + + seq_exp: $ => seq('(', $._exp, repeat(seq(';', $._exp)), ')'), + + tuple_exp: $ => seq('(', $._exp, repeat1(seq(',', $._exp)), ')'), + + list_exp: $ => choice( + seq('[', ']'), + seq('[', $._exp, repeat(seq(',', $._exp)), ']'), + ), + + vector_exp: $ => choice( + seq('#[', ']'), + seq('#[', $._exp, repeat(seq(',', $._exp)), ']'), + ), + + let_exp: $ => seq( + 'let', + repeat(choice($._ldec, ';', $.local_dec_let)), + 'in', + $._exp, repeat(seq(';', $._exp)), + 'end', + ), + + local_dec_let: $ => seq( + 'local', + repeat(choice($._ldec, ';', $.local_dec_let)), + 'in', + repeat(choice($._ldec, ';', $.local_dec_let)), + 'end', + ), + + // Constants + + _constant: $ => choice( + $.int_constant, + $.word_constant, + $.float_constant, + $.char_constant, + $.string_constant, + ), + + int_constant: $ => INT, + + word_constant: $ => WORD, + + float_constant: $ => FLOAT, + + char_constant: $ => $.character, + + string_constant: $ => $.string, + + // Types + + _ty: $ => choice( + $.tuple_ty, + $.arrow_ty, + $._ty_, + ), + + tuple_ty: $ => seq($._ty_, repeat1(seq('*', $._ty_))), + + arrow_ty: $ => prec.right(seq($._ty, '->', $._ty)), + + _ty_: $ => choice( + $.var_ty, + $.rec_ty, + $.mark_ty, + $.paren_ty, + ), + + var_ty: $ => $.tyvar, + + rec_ty: $ => choice( + seq('{', '}'), + seq('{', $.tlabel, repeat(seq(',', $.tlabel)), '}') + ), + + tlabel: $ => seq($.selector, ':', $._ty), + + mark_ty: $ => seq( + optional(choice( + $._ty_, + seq('(', $._ty, repeat1(seq(',', $._ty)), ')'), + )), + $.con_ty, + ), + + con_ty: $ => $.qident, + + paren_ty: $ => seq('(', $._ty, ')'), + + // Utils + + tyvar_seq: $ => choice( + $.tyvar, + seq('(', $.tyvar, repeat1(seq(',', $.tyvar)), ')'), + ), + + selector: $ => choice($._full_ident, /\d+/), + + tyvar: $ => /'[A-Za-z0-9_']+/, + + ident: $ => /[A-Za-z][A-Za-z0-9_']*/, + + symbolic: $ => choice( + /[!%&$#+\-/:<=>?@\\~`^|*][!%&$#+\-/:<=>?@\\~`^|*][!%&$#+\-/:<=>?@\\~`^|*]+/, + /[!%&$#+/<>?@\\~`^|*][!%&$#+\-/:<=>?@\\~`^|*]/, + /[\-:=][!%&$#+\-/:<=?@\\~`^|*]/, + /[!%&$+\-/<=>?@\\~`^*]/, + ), + + _full_ident: $ => choice($.ident, $.symbolic), + + qident: $ => seq(repeat(seq($.ident, '.')), $._full_ident), + }, +}); diff --git a/tree-sitters/prolog-tree-sitter/README.md b/tree-sitters/prolog-tree-sitter/README.md new file mode 100644 index 0000000..264d4c9 --- /dev/null +++ b/tree-sitters/prolog-tree-sitter/README.md @@ -0,0 +1 @@ +source: https://github.com/Rukiza/tree-sitter-prolog \ No newline at end of file diff --git a/tree-sitters/prolog-tree-sitter/grammar.js b/tree-sitters/prolog-tree-sitter/grammar.js new file mode 100644 index 0000000..258a3e8 --- /dev/null +++ b/tree-sitters/prolog-tree-sitter/grammar.js @@ -0,0 +1,460 @@ +const PREC = { + clause: 1200, // xfx + dcg: 1200, // xfx + + directive: 1200, // fx + qh_operator: 1200, // fx // TODO: find out what this does. + + dynamic: 1150, // fx + discontiguous: 1150, // fx + initialization: 1150, // fx + meta_predicate: 1150, // fx + module_transparent: 1150, // fx + multifile: 1150, // fx + public: 1150, // fx + thread_local: 1150, // fx + thread_initialization: 1150, // fx + volitile: 1150, // fx + + list_sperator: 1105, // xfy + + disjunction: 1100, // xfy + + implies: 1050, // xfy + soft_cut_implies: 1050, // xfy + + conjunction: 1000, // xfy + + colon_equals: 990, // xfx + + negation: 900, // fy + + less_than: 700, // xfx + assign: 700, // xfx + univ: 700, // xfx + compund_equals: 700, // xfx + compund_not_equals: 700, // xfx + equation_equals: 700, // xfx + less_than_equals: 700, // xfx + equals: 700, // xfx + equation_not_equals: 700, // xfx + greater_than: 700, // xfx + greater_than_equals: 700, // xfx + compound_less_than: 700, // xfx + compound_less_than_equals: 700, // xfx + compound_greater_than: 700, // xfx + compound_greater_than_equals: 700, // xfx + not_assigned: 700, // xfx + not_equals: 700, // xfx + as: 700, // xfx + is: 700, // xfx + partial_unification_dict: 700, // xfx + sub_dict: 700, // xfx + cql_table: 700, // xfx + + coloun: 600, // xfy + addition: 500, // yfx + subtraction: 500, // yfx + bitwise_or: 500, // yfx + bitwise_and: 500, // yfx + bitwise_xor: 500, // yfx + + question_mark: 500, // fx + + multipcation: 400, // yfx + divistion: 400, // yfx + integer_division: 400, // yfx + div: 400, // yfx + rdiv: 400, // yfx + bitwise_shift_left: 400, // yfx + bitwise_shift_right: 400, // yfx + mod: 400, // yfx + rem: 400, // yfx + + exponentiation_float: 200, // xfx + + exponentiation: 200, // xfy + + bitwise_negation: 200, //fy + positive: 200, //fy + negative: 200, //fy + + dict_inspection: 100, // yfx + + remander_deterministic: 1 // fx + +} + +const ops = [ + ':-', + '-->', + '?-', + 'dynamic','discontiguous','initialization','meta_predicate', + 'module_transparent','multifile','public','thread_local', + 'thread_initialization','volatile', + '|', + ';', + '->','*->', + // No ',' + ':=', + '\\+','<','=','=..','=@=','\\=@=','=:=','=<','==','=\\=','>','>=','@<','@=<', + '@>','@>=','\\=','\\==','as','is','>:<',':<', + ':', + '+', '-', '/\\', '\\/', 'xor', + '?', + '*', '/', '//', 'div', 'rdiv', '<<', '>>', 'mod', 'rem', + '**', + '^', + '\\', + '.', + '$' +]; + + +module.exports = grammar({ + name: 'prolog', + + extras: $ => [ + /\s/, '\n', '\r', + $.comment + ], + + conflicts: $ => [ + [$.infix_operator, $.infix_operator], + [$.infix_operator, $._prefix_non_associative], + //[$._infix_non_associative, $._infix_non_associative] + ], + + word: $ => $.atom, + + superTypes: $ => [ + $.atomic, + $.variable, + $.compound_term, + $.bracketed_term, + $.end + ], + + rules: { + // TODO: add the actual grammar rules + source_file: $ => repeat( + //$._expression_statement//, + //$._declaration_statement + choice( + field('declarive', $.directive_term), + field('clause', $.clause_term) + ) + ), + + // Change + + directive_term: $ => seq( + alias(':-', $.operator), + $._term, + $.end + ), + + + clause_term: $ => seq( + $._term, + $.end + ), + + _term: $ => prec(1, choice( + // Define Term + $.atomic, + $.variable, + $.compound_term, + $.bracketed_term, + $.dict + )), + + _arg_term: $ => prec(2, choice( + $.atomic, + $.variable, + alias($._compound_term_arg, $.compound_term), + $.bracketed_term + )), + + bracketed_term: $ => seq( + field('open', alias('(', $.bracket)), + $._term, + field('close', alias(')', $.bracket)), + ), + + end: $ => choice( + /[\n|\r]*\.\s*\n*/, + /[\n|\r]*\.\s*[\r\n]*/ + ), + + atomic: $ => choice( + $.number, + //$.negaltive_number, + $.string, + $.quoted_atom, + $.atom, + alias('!', $.cut) + ), + + variable: $ => + // Define Veriable + /[A-Z_][a-zA-Z0-9_]*/, + + compound_term: $ => prec(1, choice( + $.functional_notation, + $._non_arg_operator, // Special case for ',' not being in arguments without (). + $._operator_notation, + $._list_notation, + $._curly_bracketed_notation, + )), + + _compound_term_arg: $ => prec(2, choice( + $.functional_notation, + $._operator_notation, + $._list_notation, + $._curly_bracketed_notation, + )), + + functional_notation: $ => seq( + field('name', $.atom), + field('open', alias('(', $.bracket)),//$.open_ct, + field('arguments', $.args), + field('close', alias(')', $.bracket))//$.close_ct + ), + + _operator_notation: $ => choice( + $._prefix_notation, + $._infix_notation, + $._postfix_notation + //'thing that should not be matched' + ), + + _list_notation: $ => $.list, + + _curly_bracketed_notation: $ => $.curly_bracket_term, + + _prefix_notation: $ => field('prefix', alias(choice( + // Define prefx notation` + $._prefix_non_associative, // fx + $._prefix_right_associative // fy + ), $.prefix_operator)), + + _infix_notation: $ => ( + // Define infix notation + field('infix', $.infix_operator) + ), + + _postfix_notation: $ => field('postfix', alias(choice( + // Define postfix notation - Non in SWI spec I can see + // Need dynamic defintion of operators + //$._postfix_non_associative, // xf + //$._postfix_left_associative // yf + ), $.postfix_operator)), + + + infix_operator: $ => { // xfx + + const table = [ + [prec, PREC.clause, ':-'], // xfx + [prec, PREC.dcg, '-->'], // xfx + + [prec.right, PREC.list_sperator, '|'], // xfy + + [prec.right, PREC.disjunction, ';'], // xfy + + [prec.right, PREC.implies, '->'], // xfy + [prec.right, PREC.soft_cut_implies, '*->'], // xfy + + [prec, PREC.colon_equals, ':='], // xfx + + [prec, PREC.less_than, '<'], // xfx + [prec, PREC.assign, '='], // xfx + [prec, PREC.univ, '=..'], // xfx + [prec, PREC.compund_equals, '=@='], // xfx + [prec, PREC.compund_not_equals, '\\=@='], // xfx + [prec, PREC.equation_equals, '=:='], // xfx + [prec, PREC.less_than_equals, '=<'], // xfx + [prec, PREC.equals, '=='], // xfx + [prec, PREC.equation_not_equals, '=\\='], // xfx + [prec, PREC.greater_than, '>'], // xfx + [prec, PREC.greater_than_equals, '>='], // xfx + [prec, PREC.compound_less_than, '@<'], // xfx + [prec, PREC.compound_less_than_equals, '@=<'], // xfx + [prec, PREC.compound_greater_than, '@>'], // xfx + [prec, PREC.compound_greater_than_equals, '@>='], // xfx + [prec, PREC.not_assigned, '\\='], // xfx + [prec, PREC.not_equals, '\\=='], // xfx + [prec, PREC.as, 'as'], // xfx + [prec, PREC.is, 'is'], // xfx + [prec, PREC.partial_unification_dict, '>:<'], // xfx + [prec, PREC.sub_dict, ':<'], // xfx + [prec, PREC.cql_table, '::'], // xfx + + [prec.right, PREC.coloun, ':'], // xfy + + [prec.left, PREC.addition, '+'], // yfx + [prec.left, PREC.subtraction, '-'], // yfx + [prec.left, PREC.bitwise_or, '\\/'], // yfx + [prec.left, PREC.bitwise_and, '/\\'], // yfx + [prec.left, PREC.bitwise_xor, 'xor'], // yfx + + [prec.left, PREC.multipcation, '*'], // yfx + [prec.left, PREC.divistion, '/'], // yfx + [prec.left, PREC.integer_division, '//'], // yfx + [prec.left, PREC.div, 'div'], // yfx + [prec.left, PREC.rdiv, 'rdiv'], // yfx + [prec.left, PREC.bitwise_shift_left, '<<'], // yfx + [prec.left, PREC.bitwise_shift_right, '>>'], // yfx + [prec.left, PREC.mod, 'mod'], // yfx + [prec.left, PREC.rem, 'rem'], // yfx + + [prec, PREC.exponentiation_float, '**'], // xfx + + [prec.right, PREC.exponentiation, '^'], // xfy + + [prec.left, PREC.dict_inspection, '.'], // yfx + ]; + + return choice(...table.map(([fn, precidence, operator]) => fn(precidence, seq( + field('left', $._term), + field('operator', alias(operator, $.operator)), + field('right', $._term) + )))) + }, + + _non_arg_operator: $ => prec.right(PREC.conjunction, seq( + field('left', $._term), + field('operator', alias(',', $.operator)), + field('right', $._term) + )), + + // Prefix operaters seporated.. Unsure if I should colapes them at this stage. + _prefix_non_associative: $ => { + const table = [ + [PREC.dynamic, 'dynamic'], + [PREC.discontiguous, 'discontiguous'], // fx + [PREC.initialization, 'initialization'], // fx + [PREC.meta_predicate, 'meta_predicate'], // fx + [PREC.module_transparent, 'module_transparent'], // fx + [PREC.multifile, 'multifile'], // fx + [PREC.public, 'public'], // fx + [PREC.thread_local, 'thread_local'], // fx + [PREC.thread_initialization, 'thread_initialization'], // fx + [PREC.volitile, 'volitile'], + [PREC.directive, 'directive'], + [PREC.qh_operator, '?-'], + // [PREC.directive, ':-'], // fx leaving out for the time being + [PREC.question_mark, '?'], + [PREC.remander_deterministic, '$'] // fx + ]; + + return choice(...table.map(([precidence, operator]) => prec(precidence, seq( + field('operator', alias(operator, $.operator)), + field('right', $._term) + )))) + }, + + _prefix_right_associative: $=> { + const table = [ + [PREC.negation, '/+'], // fy + [PREC.bitwise_negation, '/'], //fy + [PREC.positive, '+'], //fy + [PREC.negative, '-'], //fy + ]; + + return choice(...table.map(([precidence, operator]) => prec.right(precidence, seq( + field('operator', alias(operator, $.operator)), + field('right', $._term) + )))) + }, + + dict: $ => seq( + choice( + $.atom, + $.variable + ), + '{', + seq($.dict_value, + repeat( + seq( + ',', + $.dict_value + ) + ) + ), + '}' + ), + + dict_value: $ => seq( + $.atom, + token.immediate(':'), + $._arg_term + ), + + number: $ => ( + /0[bB][01](_?[01])*|0[oO]?[0-7](_?[0-7])*|(0[dD])?\d(_?\d)*|0[xX][0-9a-fA-F](_?[0-9a-fA-F])*/ + ), + + atom: $ => ( + /[a-z][a-zA-Z0-9_]*/ + ), + + quoted_atom: $ => token(seq('\'', /.*/, '\'')), + + string: $ => token(seq('\"', /.*/, '\"')), + + comment: $ => token(choice( + seq('%', /.*/), + seq( + '/*', + /[^*]*\*+([^/*][^*]*\*+)*/, + '/' + ) + )), + + list: $ => prec.right(seq( + field('open_list', alias('[', $.square_brackets)), // open list + field('arguments', optional($.args)), + field('close_list', alias(']', $.square_brackets)) // close list + )), + + curly_bracket_term: $ => prec.right(seq( + field('open_cb', alias('{', $.curly_bracket)), + optional(seq($._term, + repeat( + prec.right(seq( // TODO: Work out if correct + ',', + $._term + )) + ))), + field('close_cb', alias('}', $.curly_bracket)) + )), + + _arg: $ => prec.left(choice( + $._operator_pred_list, + $._arg_term + )), + + args: $ => prec.right(seq( + $._arg, + repeat( + prec.left(seq( // TODO: Work out if correct + ',', + $._arg + )) + ), + )), + + // Operators avliable in a predicate or list. + _operator_pred_list: $ => { + return alias(choice(...ops.map((op) => + op + )), $.operator) + } + + }, + user_defined_operators: $ => { + + } + +}); diff --git a/tree-sitters/ruby-tree-sitter/README.md b/tree-sitters/ruby-tree-sitter/README.md new file mode 100644 index 0000000..2a5a42f --- /dev/null +++ b/tree-sitters/ruby-tree-sitter/README.md @@ -0,0 +1 @@ +source: https://github.com/tree-sitter/tree-sitter-ruby \ No newline at end of file diff --git a/tree-sitters/ruby-tree-sitter/grammar.js b/tree-sitters/ruby-tree-sitter/grammar.js new file mode 100644 index 0000000..634e0bc --- /dev/null +++ b/tree-sitters/ruby-tree-sitter/grammar.js @@ -0,0 +1,1286 @@ +/// +// @ts-check + +const PREC = { + COMMENT: -2, + CURLY_BLOCK: 1, + DO_BLOCK: -1, + + AND: -2, + OR: -2, + NOT: 5, + DEFINED: 10, + ALIAS: 11, + ASSIGN: 15, + RESCUE: 16, + CONDITIONAL: 20, + RANGE: 25, + BOOLEAN_OR: 30, + BOOLEAN_AND: 35, + RELATIONAL: 40, + COMPARISON: 45, + BITWISE_OR: 50, + BITWISE_AND: 55, + CALL: 56, + SHIFT: 60, + ADDITIVE: 65, + MULTIPLICATIVE: 70, + UNARY_MINUS: 75, + EXPONENTIAL: 80, + COMPLEMENT: 85, +}; + +const IDENTIFIER_CHARS = /[^\x00-\x1F\s:;`"'@$#.,|^&<=>+\-*/\\%?!~()\[\]{}]*/; +const LOWER_ALPHA_CHAR = /[^\x00-\x1F\sA-Z0-9:;`"'@$#.,|^&<=>+\-*/\\%?!~()\[\]{}]/; +const ALPHA_CHAR = /[^\x00-\x1F\s0-9:;`"'@$#.,|^&<=>+\-*/\\%?!~()\[\]{}]/; + +module.exports = grammar({ + name: 'ruby', + inline: $ => [$._arg_rhs, $._call_operator], + externals: $ => [ + $._line_break, + $._no_line_break, + + // Delimited literals + $.simple_symbol, + $._string_start, + $._symbol_start, + $._subshell_start, + $._regex_start, + $._string_array_start, + $._symbol_array_start, + $._heredoc_body_start, + $.string_content, + $.heredoc_content, + $._string_end, + $.heredoc_end, + $.heredoc_beginning, + + // Tokens that require lookahead + '/', + $._block_ampersand, + $._splat_star, + $._unary_minus, + $._unary_minus_num, + $._binary_minus, + $._binary_star, + $._singleton_class_left_angle_left_langle, + $.hash_key_symbol, + $._identifier_suffix, + $._constant_suffix, + $._hash_splat_star_star, + $._binary_star_star, + $._element_reference_bracket, + $._short_interpolation, + ], + + extras: $ => [ + $.comment, + $.heredoc_body, + /\s/, + /\\\r?\n/, + ], + + word: $ => $.identifier, + + supertypes: $ => [ + $._statement, + $._arg, + $._call_operator, + $._method_name, + $._expression, + $._variable, + $._primary, + $._simple_numeric, + $._lhs, + $._nonlocal_variable, + $._pattern_top_expr_body, + $._pattern_expr, + $._pattern_expr_basic, + $._pattern_primitive, + $._pattern_constant, + ], + + rules: { + program: $ => seq( + optional($._statements), + optional( + choice( + seq(/__END__[\r\n]/, $.uninterpreted), + seq('__END__', alias('', $.uninterpreted)), + ), + ), + ), + + uninterpreted: _ => /(.|\s)*/, + + block_body: $ => $._statements, + + _statements: $ => choice( + seq( + repeat1(choice( + seq($._statement, $._terminator), + $.empty_statement, + )), + optional($._statement), + ), + $._statement, + ), + + begin_block: $ => seq('BEGIN', '{', optional($._statements), '}'), + end_block: $ => seq('END', '{', optional($._statements), '}'), + + _statement: $ => choice( + $.undef, + $.alias, + $.if_modifier, + $.unless_modifier, + $.while_modifier, + $.until_modifier, + $.rescue_modifier, + $.begin_block, + $.end_block, + $._expression, + ), + + method: $ => seq('def', $._method_rest), + + singleton_method: $ => seq( + 'def', + seq( + choice( + field('object', $._variable), + seq('(', field('object', $._arg), ')'), + ), + choice('.', '::'), + ), + $._method_rest, + ), + + _method_rest: $ => seq( + field('name', $._method_name), + choice( + $._body_expr, + seq( + field('parameters', alias($.parameters, $.method_parameters)), + choice( + seq(optional($._terminator), optional(field('body', $.body_statement)), 'end'), + $._body_expr, + ), + + ), + seq( + optional( + field('parameters', alias($.bare_parameters, $.method_parameters)), + ), + $._terminator, + optional(field('body', $.body_statement)), + 'end', + ), + ), + ), + + rescue_modifier_arg: $ => prec(PREC.RESCUE, + seq( + field('body', $._arg), + 'rescue', + field('handler', $._arg), + ), + ), + + rescue_modifier_expression: $ => prec(PREC.RESCUE, + seq( + field('body', $._expression), + 'rescue', + field('handler', $._arg), + ), + ), + + _body_expr: $ => + seq( + '=', + field('body', + choice( + $._arg, + alias($.rescue_modifier_arg, $.rescue_modifier), + )), + ), + + + parameters: $ => seq( + '(', + commaSep($._formal_parameter), + ')', + ), + + bare_parameters: $ => seq( + $._simple_formal_parameter, + repeat(seq(',', $._formal_parameter)), + ), + + block_parameters: $ => seq( + '|', + seq(commaSep($._formal_parameter), optional(',')), + optional(seq(';', sep1(field('locals', $.identifier), ','))), // Block shadow args e.g. {|; a, b| ...} + '|', + ), + + _formal_parameter: $ => choice( + $._simple_formal_parameter, + alias($.parameters, $.destructured_parameter), + ), + + _simple_formal_parameter: $ => choice( + $.identifier, + $.splat_parameter, + $.hash_splat_parameter, + $.hash_splat_nil, + $.forward_parameter, + $.block_parameter, + $.keyword_parameter, + $.optional_parameter, + ), + + forward_parameter: _ => '...', + + splat_parameter: $ => prec.right(-2, seq( + '*', + field('name', optional($.identifier)), + )), + hash_splat_parameter: $ => seq( + '**', + field('name', optional($.identifier)), + ), + hash_splat_nil: _ => seq('**', 'nil'), + block_parameter: $ => seq( + '&', + field('name', optional($.identifier)), + ), + keyword_parameter: $ => prec.right(PREC.BITWISE_OR + 1, seq( + field('name', $.identifier), + token.immediate(':'), + field('value', optional($._arg)), + )), + optional_parameter: $ => prec(PREC.BITWISE_OR + 1, seq( + field('name', $.identifier), + '=', + field('value', $._arg), + )), + + class: $ => seq( + 'class', + field('name', choice($.constant, $.scope_resolution)), + choice( + seq(field('superclass', $.superclass), $._terminator), + optional($._terminator), + ), + optional(field('body', $.body_statement)), + 'end', + ), + + superclass: $ => seq('<', $._expression), + + singleton_class: $ => seq( + 'class', + alias($._singleton_class_left_angle_left_langle, '<<'), + field('value', $._arg), + $._terminator, + optional(field('body', $.body_statement)), + 'end', + ), + + module: $ => seq( + 'module', + field('name', choice($.constant, $.scope_resolution)), + optional($._terminator), + optional(field('body', $.body_statement)), + 'end', + ), + + return_command: $ => prec.left(seq('return', alias($.command_argument_list, $.argument_list))), + yield_command: $ => prec.left(seq('yield', alias($.command_argument_list, $.argument_list))), + break_command: $ => prec.left(seq('break', alias($.command_argument_list, $.argument_list))), + next_command: $ => prec.left(seq('next', alias($.command_argument_list, $.argument_list))), + return: $ => prec.left(seq('return', optional($.argument_list))), + yield: $ => prec.left(seq('yield', optional($.argument_list))), + break: $ => prec.left(seq('break', optional($.argument_list))), + next: $ => prec.left(seq('next', optional($.argument_list))), + redo: $ => prec.left(seq('redo', optional($.argument_list))), + retry: $ => prec.left(seq('retry', optional($.argument_list))), + + if_modifier: $ => prec(PREC.RESCUE, seq( + field('body', $._statement), + 'if', + field('condition', $._expression), + )), + + unless_modifier: $ => prec(PREC.RESCUE, seq( + field('body', $._statement), + 'unless', + field('condition', $._expression), + )), + + while_modifier: $ => prec(PREC.RESCUE, seq( + field('body', $._statement), + 'while', + field('condition', $._expression), + )), + + until_modifier: $ => prec(PREC.RESCUE, seq( + field('body', $._statement), + 'until', + field('condition', $._expression), + )), + + rescue_modifier: $ => prec(PREC.RESCUE, seq( + field('body', $._statement), + 'rescue', + field('handler', $._expression), + )), + + while: $ => seq( + 'while', + field('condition', $._statement), + field('body', $.do), + ), + + until: $ => seq( + 'until', + field('condition', $._statement), + field('body', $.do), + ), + + for: $ => seq( + 'for', + field('pattern', choice($._lhs, $.left_assignment_list)), + field('value', $.in), + field('body', $.do), + ), + + in: $ => seq('in', $._arg), + do: $ => seq( + choice('do', $._terminator), + optional($._statements), + 'end', + ), + + case: $ => seq( + 'case', + optional(seq(optional($._line_break), field('value', $._statement))), + optional($._terminator), + repeat($.when), + optional($.else), + 'end', + ), + + case_match: $ => seq( + 'case', + seq(optional($._line_break), field('value', $._statement)), + optional($._terminator), + repeat1(field('clauses', $.in_clause)), + optional(field('else', $.else)), + 'end', + ), + + when: $ => seq( + 'when', + commaSep1(field('pattern', $.pattern)), + choice($._terminator, field('body', $.then)), + ), + + in_clause: $ => seq( + 'in', + field('pattern', $._pattern_top_expr_body), + field('guard', optional($._guard)), + choice($._terminator, field('body', $.then)), + ), + + pattern: $ => choice($._arg, $.splat_argument), + + _guard: $ => choice( + $.if_guard, + $.unless_guard, + ), + + if_guard: $ => seq( + 'if', + field('condition', $._expression), + ), + + unless_guard: $ => seq( + 'unless', + field('condition', $._expression), + ), + + _pattern_top_expr_body: $ => prec(-1, choice( + $._pattern_expr, + alias($._array_pattern_n, $.array_pattern), + alias($._find_pattern_body, $.find_pattern), + alias($._hash_pattern_body, $.hash_pattern), + )), + + _array_pattern_n: $ => prec.right(choice( + seq($._pattern_expr, alias(',', $.splat_parameter)), + seq($._pattern_expr, ',', choice($._pattern_expr, $._array_pattern_n)), + seq($.splat_parameter, repeat(seq(',', $._pattern_expr))), + )), + + _pattern_expr: $ => choice( + $.as_pattern, + $._pattern_expr_alt, + ), + + as_pattern: $ => seq(field('value', $._pattern_expr), '=>', field('name', $.identifier)), + + _pattern_expr_alt: $ => choice( + $.alternative_pattern, + $._pattern_expr_basic, + ), + + alternative_pattern: $ => seq(field('alternatives', $._pattern_expr_basic), repeat1(seq('|', field('alternatives', $._pattern_expr_basic)))), + + _array_pattern_body: $ => choice( + $._pattern_expr, + $._array_pattern_n, + ), + + array_pattern: $ => prec.right(-1, choice( + seq('[', optional($._array_pattern_body), ']'), + seq(field('class', $._pattern_constant), token.immediate('['), optional($._array_pattern_body), ']'), + seq(field('class', $._pattern_constant), token.immediate('('), optional($._array_pattern_body), ')'), + )), + + _find_pattern_body: $ => seq($.splat_parameter, repeat1(seq(',', $._pattern_expr)), ',', $.splat_parameter), + find_pattern: $ => choice( + seq('[', $._find_pattern_body, ']'), + seq(field('class', $._pattern_constant), token.immediate('['), $._find_pattern_body, ']'), + seq(field('class', $._pattern_constant), token.immediate('('), $._find_pattern_body, ')'), + ), + + _hash_pattern_body: $ => prec.right(choice( + seq(commaSep1($.keyword_pattern), optional(',')), + seq(commaSep1($.keyword_pattern), ',', $._hash_pattern_any_rest), + $._hash_pattern_any_rest, + )), + + keyword_pattern: $ => prec.right(-1, seq( + field('key', + choice( + alias($.identifier, $.hash_key_symbol), + alias($.constant, $.hash_key_symbol), + alias($.identifier_suffix, $.hash_key_symbol), + alias($.constant_suffix, $.hash_key_symbol), + $.string, + ), + ), + token.immediate(':'), + optional(field('value', $._pattern_expr)), + )), + + _hash_pattern_any_rest: $ => choice($.hash_splat_parameter, $.hash_splat_nil), + + hash_pattern: $ => prec.right(-1, choice( + seq('{', optional($._hash_pattern_body), '}'), + seq(field('class', $._pattern_constant), token.immediate('['), $._hash_pattern_body, ']'), + seq(field('class', $._pattern_constant), token.immediate('('), $._hash_pattern_body, ')'), + )), + + _pattern_expr_basic: $ => prec.right(-1, choice( + $._pattern_value, + $.identifier, + $.array_pattern, + $.find_pattern, + $.hash_pattern, + $.parenthesized_pattern, + )), + + parenthesized_pattern: $ => seq('(', $._pattern_expr, ')'), + + _pattern_value: $ => prec.right(-1, choice( + $._pattern_primitive, + alias($._pattern_range, $.range), + $.variable_reference_pattern, + $.expression_reference_pattern, + $._pattern_constant, + )), + + _pattern_range: $ => { + const begin = field('begin', $._pattern_primitive); + const end = field('end', $._pattern_primitive); + const operator = field('operator', choice('..', '...')); + return choice( + seq(begin, operator, end), + seq(operator, end), + seq(begin, operator), + ); + }, + + _pattern_primitive: $ => choice( + $._pattern_literal, + $._pattern_lambda, + ), + + _pattern_lambda: $ => prec.right(-1, $.lambda), + + _pattern_literal: $ => prec.right(-1, choice( + $._literal, + $.string, + $.subshell, + $.heredoc_beginning, + $.regex, + $.string_array, + $.symbol_array, + $._keyword_variable, + )), + + _keyword_variable: $ => prec.right(-1, choice( + $.nil, + $.self, + $.true, + $.false, + $.line, + $.file, + $.encoding, + )), + + line: _ => '__LINE__', + file: _ => '__FILE__', + encoding: _ => '__ENCODING__', + + variable_reference_pattern: $ => seq('^', field('name', choice($.identifier, $._nonlocal_variable))), + + expression_reference_pattern: $ => seq('^', '(', field('value', $._expression), ')'), + + _pattern_constant: $ => prec.right(-1, choice( + $.constant, + alias($._pattern_constant_resolution, $.scope_resolution), + )), + + _pattern_constant_resolution: $ => seq( + optional(field('scope', $._pattern_constant)), + '::', + field('name', $.constant), + ), + + if: $ => seq( + 'if', + field('condition', $._statement), + choice($._terminator, field('consequence', $.then)), + field('alternative', optional(choice($.else, $.elsif))), + 'end', + ), + + unless: $ => seq( + 'unless', + field('condition', $._statement), + choice($._terminator, field('consequence', $.then)), + field('alternative', optional(choice($.else, $.elsif))), + 'end', + ), + + elsif: $ => seq( + 'elsif', + field('condition', $._statement), + choice($._terminator, field('consequence', $.then)), + field('alternative', optional(choice($.else, $.elsif))), + ), + + else: $ => seq( + 'else', + optional($._terminator), + optional($._statements), + ), + + then: $ => choice( + seq( + $._terminator, + $._statements, + ), + seq( + optional($._terminator), + 'then', + optional($._statements), + ), + ), + + begin: $ => seq('begin', optional($._terminator), optional($._body_statement), 'end'), + + ensure: $ => seq('ensure', optional($._statements)), + + rescue: $ => seq( + 'rescue', + field('exceptions', optional($.exceptions)), + field('variable', optional($.exception_variable)), + choice( + $._terminator, + field('body', $.then), + ), + ), + + exceptions: $ => commaSep1(choice($._arg, $.splat_argument)), + + exception_variable: $ => seq('=>', $._lhs), + + body_statement: $ => $._body_statement, + + _body_statement: $ => choice( + seq($._statements, repeat(choice($.rescue, $.else, $.ensure))), + seq(optional($._statements), repeat1(choice($.rescue, $.else, $.ensure))), + ), + + // Method calls without parentheses (aka "command calls") are only allowed + // in certain positions, like the top-level of a statement, the condition + // of a postfix control-flow operator like `if`, or as the value of a + // control-flow statement like `return`. In many other places, they're not + // allowed. + // + // Because of this distinction, a lot of rules have two variants: the + // normal variant, which can appear anywhere that an expression is valid, + // and the "command" varaint, which is only valid in a more limited set of + // positions, because it can contain "command calls". + // + // The `_expression` rule can appear in relatively few places, but can + // contain command calls. The `_arg` rule can appear in many more places, + // but cannot contain command calls (unless they are wrapped in parens). + // This naming convention is based on Ruby's standard grammar. + _expression: $ => choice( + alias($.command_binary, $.binary), + alias($.command_unary, $.unary), + alias($.command_assignment, $.assignment), + alias($.command_operator_assignment, $.operator_assignment), + alias($.command_call, $.call), + alias($.command_call_with_block, $.call), + prec.left(alias($._chained_command_call, $.call)), + alias($.return_command, $.return), + alias($.yield_command, $.yield), + alias($.break_command, $.break), + alias($.next_command, $.next), + $.match_pattern, + $.test_pattern, + $._arg, + ), + + match_pattern: $ => prec(100, seq(field('value', $._arg), '=>', field('pattern', $._pattern_top_expr_body))), + + test_pattern: $ => prec(100, seq(field('value', $._arg), 'in', field('pattern', $._pattern_top_expr_body))), + + _arg: $ => choice( + alias($._unary_minus_pow, $.unary), + $._primary, + $.assignment, + $.operator_assignment, + $.conditional, + $.range, + $.binary, + $.unary, + ), + + _unary_minus_pow: $ => seq(field('operator', alias($._unary_minus_num, '-')), field('operand', alias($._pow, $.binary))), + _pow: $ => prec.right(PREC.EXPONENTIAL, seq(field('left', $._simple_numeric), field('operator', alias($._binary_star_star, '**')), field('right', $._arg))), + + _primary: $ => choice( + $.parenthesized_statements, + $._lhs, + alias($._function_identifier_call, $.call), + $.call, + $.array, + $.string_array, + $.symbol_array, + $.hash, + $.subshell, + $._literal, + $.string, + $.character, + $.chained_string, + $.regex, + $.lambda, + $.method, + $.singleton_method, + $.class, + $.singleton_class, + $.module, + $.begin, + $.while, + $.until, + $.if, + $.unless, + $.for, + $.case, + $.case_match, + $.return, + $.yield, + $.break, + $.next, + $.redo, + $.retry, + alias($.parenthesized_unary, $.unary), + $.heredoc_beginning, + ), + + parenthesized_statements: $ => seq('(', optional($._statements), ')'), + + element_reference: $ => prec.left(1, seq( + field('object', $._primary), + alias($._element_reference_bracket, '['), + optional($._argument_list_with_trailing_comma), + ']', + optional(field('block', choice($.block, $.do_block))), + )), + + scope_resolution: $ => prec.left(PREC.CALL + 1, seq( + choice( + '::', + seq(field('scope', $._primary), token.immediate('::')), + ), + field('name', $.constant), + )), + + _call_operator: _ => choice('.', '&.', token.immediate('::')), + _call: $ => prec.left(PREC.CALL, seq( + field('receiver', $._primary), + field('operator', $._call_operator), + field('method', choice($.identifier, $.operator, $.constant, $._function_identifier)), + )), + + command_call: $ => seq( + choice( + $._call, + $._chained_command_call, + field('method', choice( + $._variable, + $._function_identifier, + )), + ), + field('arguments', alias($.command_argument_list, $.argument_list)), + ), + + command_call_with_block: $ => { + const receiver = choice( + $._call, + field('method', choice($._variable, $._function_identifier)), + ); + const args = field('arguments', alias($.command_argument_list, $.argument_list)); + const block = field('block', $.block); + const doBlock = field('block', $.do_block); + return choice( + seq(receiver, prec(PREC.CURLY_BLOCK, seq(args, block))), + seq(receiver, prec(PREC.DO_BLOCK, seq(args, doBlock))), + ); + }, + + _chained_command_call: $ => seq( + field('receiver', alias($.command_call_with_block, $.call)), + field('operator', $._call_operator), + field('method', choice($.identifier, $._function_identifier, $.operator, $.constant)), + ), + + call: $ => { + const receiver = choice( + $._call, + field('method', choice( + $._variable, $._function_identifier, + )), + ); + + const args = field('arguments', $.argument_list); + const receiverArguments = + seq( + choice( + receiver, + prec.left(PREC.CALL, seq( + field('receiver', $._primary), + field('operator', $._call_operator), + )), + ), + args, + ); + + const block = field('block', $.block); + const doBlock = field('block', $.do_block); + return choice( + receiverArguments, + prec(PREC.CURLY_BLOCK, seq(receiverArguments, block)), + prec(PREC.DO_BLOCK, seq(receiverArguments, doBlock)), + prec(PREC.CURLY_BLOCK, seq(receiver, block)), + prec(PREC.DO_BLOCK, seq(receiver, doBlock)), + ); + }, + + command_argument_list: $ => prec.right(commaSep1($._argument)), + + argument_list: $ => prec.right(seq( + token.immediate('('), + optional($._argument_list_with_trailing_comma), + ')', + )), + + _argument_list_with_trailing_comma: $ => prec.right(seq( + commaSep1($._argument), + optional(','), + )), + + _argument: $ => prec.left(choice( + $._expression, + $.splat_argument, + $.hash_splat_argument, + $.forward_argument, + $.block_argument, + $.pair, + )), + + forward_argument: _ => '...', + splat_argument: $ => prec.right(seq(alias($._splat_star, '*'), optional($._arg))), + hash_splat_argument: $ => prec.right(seq(alias($._hash_splat_star_star, '**'), optional($._arg))), + block_argument: $ => prec.right(seq(alias($._block_ampersand, '&'), optional($._arg))), + + do_block: $ => seq( + 'do', + optional($._terminator), + optional(seq( + field('parameters', $.block_parameters), + optional($._terminator), + )), + optional(field('body', $.body_statement)), + 'end', + ), + + block: $ => prec(PREC.CURLY_BLOCK, seq( + '{', + field('parameters', optional($.block_parameters)), + optional(field('body', $.block_body)), + '}', + )), + + _arg_rhs: $ => choice($._arg, alias($.rescue_modifier_arg, $.rescue_modifier)), + assignment: $ => prec.right(PREC.ASSIGN, choice( + seq( + field('left', choice($._lhs, $.left_assignment_list)), + '=', + field('right', choice( + $._arg_rhs, + $.splat_argument, + $.right_assignment_list, + )), + ), + )), + + command_assignment: $ => prec.right(PREC.ASSIGN, + seq( + field('left', choice($._lhs, $.left_assignment_list)), + '=', + field('right', choice($._expression, alias($.rescue_modifier_expression, $.rescue_modifier))), + ), + ), + + operator_assignment: $ => prec.right(PREC.ASSIGN, seq( + field('left', $._lhs), + field('operator', choice('+=', '-=', '*=', '**=', '/=', '||=', '|=', '&&=', '&=', '%=', '>>=', '<<=', '^=')), + field('right', $._arg_rhs), + )), + + command_operator_assignment: $ => prec.right(PREC.ASSIGN, seq( + field('left', $._lhs), + field('operator', choice('+=', '-=', '*=', '**=', '/=', '||=', '|=', '&&=', '&=', '%=', '>>=', '<<=', '^=')), + field('right', choice($._expression, alias($.rescue_modifier_expression, $.rescue_modifier))), + )), + + conditional: $ => prec.right(PREC.CONDITIONAL, seq( + field('condition', $._arg), + '?', + field('consequence', $._arg), + ':', + field('alternative', $._arg), + )), + + range: $ => { + const begin = field('begin', $._arg); + const end = field('end', $._arg); + const operator = field('operator', choice('..', '...')); + return prec.right(PREC.RANGE, choice( + seq(begin, operator, end), + seq(operator, end), + seq(begin, operator), + )); + }, + + binary: $ => { + const operators = [ + [prec.left, PREC.AND, 'and'], + [prec.left, PREC.OR, 'or'], + [prec.left, PREC.BOOLEAN_OR, '||'], + [prec.left, PREC.BOOLEAN_AND, '&&'], + [prec.left, PREC.SHIFT, choice('<<', '>>')], + [prec.left, PREC.COMPARISON, choice('<', '<=', '>', '>=')], + [prec.left, PREC.BITWISE_AND, '&'], + [prec.left, PREC.BITWISE_OR, choice('^', '|')], + [prec.left, PREC.ADDITIVE, choice('+', alias($._binary_minus, '-'))], + [prec.left, PREC.MULTIPLICATIVE, choice('/', '%', alias($._binary_star, '*'))], + [prec.right, PREC.RELATIONAL, choice('==', '!=', '===', '<=>', '=~', '!~')], + [prec.right, PREC.EXPONENTIAL, alias($._binary_star_star, '**')], + ]; + + // @ts-ignore + return choice(...operators.map(([fn, precedence, operator]) => fn(precedence, seq( + field('left', $._arg), + // @ts-ignore + field('operator', operator), + field('right', $._arg), + )))); + }, + + command_binary: $ => prec.left(seq( + field('left', $._expression), + field('operator', choice('or', 'and')), + field('right', $._expression), + )), + + unary: $ => { + const operators = [ + [prec, PREC.DEFINED, 'defined?'], + [prec.right, PREC.NOT, 'not'], + [prec.right, PREC.UNARY_MINUS, choice(alias($._unary_minus, '-'), alias($._binary_minus, '-'), '+')], + [prec.right, PREC.COMPLEMENT, choice('!', '~')], + ]; + // @ts-ignore + return choice(...operators.map(([fn, precedence, operator]) => fn(precedence, seq( + // @ts-ignore + field('operator', operator), + field('operand', $._arg), + )))); + }, + + command_unary: $ => { + const operators = [ + [prec, PREC.DEFINED, 'defined?'], + [prec.right, PREC.NOT, 'not'], + [prec.right, PREC.UNARY_MINUS, choice(alias($._unary_minus, '-'), '+')], + [prec.right, PREC.COMPLEMENT, choice('!', '~')], + ]; + // @ts-ignore + return choice(...operators.map(([fn, precedence, operator]) => fn(precedence, seq( + // @ts-ignore + field('operator', operator), + field('operand', $._expression), + )))); + }, + + parenthesized_unary: $ => prec(PREC.CALL, seq( + field('operator', choice('defined?', 'not')), + field('operand', $.parenthesized_statements), + )), + + unary_literal: $ => prec.right(PREC.UNARY_MINUS, seq( + field('operator', choice(alias($._unary_minus_num, '-'), '+')), + field('operand', $._simple_numeric), + )), + + _literal: $ => choice( + $.simple_symbol, + $.delimited_symbol, + $._numeric, + ), + + _numeric: $ => choice( + $._simple_numeric, + alias($.unary_literal, $.unary), + ), + + _simple_numeric: $ => + choice( + $.integer, + $.float, + $.complex, + $.rational, + ), + + right_assignment_list: $ => prec(-1, commaSep1(choice($._arg, $.splat_argument))), + + left_assignment_list: $ => $._mlhs, + _mlhs: $ => prec.left(-1, seq( + commaSep1(choice($._lhs, $.rest_assignment, $.destructured_left_assignment)), + optional(','), + )), + destructured_left_assignment: $ => prec(-1, seq('(', $._mlhs, ')')), + + rest_assignment: $ => prec(-1, seq('*', optional($._lhs))), + + _function_identifier: $ => choice(alias($.identifier_suffix, $.identifier), alias($.constant_suffix, $.constant)), + _function_identifier_call: $ => prec.left(field('method', $._function_identifier)), + _lhs: $ => prec.left(choice( + $._variable, + $.true, + $.false, + $.nil, + $.scope_resolution, + $.element_reference, + alias($._call, $.call), + )), + + _variable: $ => prec.right(choice( + $.self, + $.super, + $._nonlocal_variable, + $.identifier, + $.constant, + )), + + operator: _ => choice( + '..', '|', '^', '&', '<=>', '==', '===', '=~', '>', '>=', '<', '<=', '+', '!=', + '-', '*', '/', '%', '!', '!~', '**', '<<', '>>', '~', '+@', '-@', '~@', '[]', '[]=', '`', + ), + + _method_name: $ => choice( + $.identifier, + $._function_identifier, + $.constant, + $.setter, + $.simple_symbol, + $.delimited_symbol, + $.operator, + $._nonlocal_variable, + ), + + _nonlocal_variable: $ => choice( + $.instance_variable, + $.class_variable, + $.global_variable, + ), + + setter: $ => seq(field('name', $.identifier), token.immediate('=')), + + undef: $ => seq('undef', commaSep1($._method_name)), + alias: $ => seq( + 'alias', + field('name', $._method_name), + field('alias', $._method_name), + ), + + comment: _ => token(prec(PREC.COMMENT, choice( + seq('#', /.*/), + seq( + /=begin.*\r?\n/, + repeat(choice( + /[^=]/, + /=[^e]/, + /=e[^n]/, + /=en[^d]/, + )), + /[\s*]*=end.*/, + ), + ))), + + integer: _ => /0[bB][01](_?[01])*|0[oO]?[0-7](_?[0-7])*|(0[dD])?\d(_?\d)*|0[xX][0-9a-fA-F](_?[0-9a-fA-F])*/, + _int_or_float: $ => choice($.integer, $.float), + float: _ => /\d(_?\d)*(\.\d)?(_?\d)*([eE][\+-]?\d(_?\d)*)?/, + complex: $ => choice( + seq($._int_or_float, token.immediate('i')), + seq(alias($._int_or_float, $.rational), token.immediate('ri')), + ), + rational: $ => seq($._int_or_float, token.immediate('r')), + super: _ => 'super', + self: _ => 'self', + true: _ => 'true', + false: _ => 'false', + nil: _ => 'nil', + + constant: _ => token(seq(/[A-Z]/, IDENTIFIER_CHARS)), + constant_suffix: $ => choice(token(seq(/[A-Z]/, IDENTIFIER_CHARS, /[?]/)), $._constant_suffix), + identifier: _ => token(seq(LOWER_ALPHA_CHAR, IDENTIFIER_CHARS)), + identifier_suffix: $ => choice(token(seq(LOWER_ALPHA_CHAR, IDENTIFIER_CHARS, /[?]/)), $._identifier_suffix), + instance_variable: _ => token(seq('@', ALPHA_CHAR, IDENTIFIER_CHARS)), + class_variable: _ => token(seq('@@', ALPHA_CHAR, IDENTIFIER_CHARS)), + + global_variable: _ => /\$(-[a-zA-Z0-9_]|[!@&`'+~=/\\,;.<>*$?:"]|[0-9]+|[a-zA-Z_][a-zA-Z0-9_]*)/, + + chained_string: $ => seq($.string, repeat1($.string)), + + character: _ => /\?(\\\S(\{[0-9A-Fa-f]*\}|[0-9A-Fa-f]*|-\S([MC]-\S)?)?|\S)/, + + interpolation: $ => choice( + seq('#{', optional($._statements), '}'), + seq($._short_interpolation, $._nonlocal_variable), + ), + + string: $ => seq( + alias($._string_start, '"'), + optional($._literal_contents), + alias($._string_end, '"'), + ), + + subshell: $ => seq( + alias($._subshell_start, '`'), + optional($._literal_contents), + alias($._string_end, '`'), + ), + + string_array: $ => seq( + alias($._string_array_start, '%w('), + optional(/\s+/), + sep(alias($._literal_contents, $.bare_string), /\s+/), + optional(/\s+/), + alias($._string_end, ')'), + ), + + symbol_array: $ => seq( + alias($._symbol_array_start, '%i('), + optional(/\s+/), + sep(alias($._literal_contents, $.bare_symbol), /\s+/), + optional(/\s+/), + alias($._string_end, ')'), + ), + + delimited_symbol: $ => seq( + alias($._symbol_start, ':"'), + optional($._literal_contents), + alias($._string_end, '"'), + ), + + regex: $ => seq( + alias($._regex_start, '/'), + optional($._literal_contents), + alias($._string_end, '/'), + ), + + heredoc_body: $ => seq( + $._heredoc_body_start, + repeat(choice( + $.heredoc_content, + $.interpolation, + $.escape_sequence, + )), + $.heredoc_end, + ), + + _literal_contents: $ => repeat1(choice( + $.string_content, + $.interpolation, + $.escape_sequence, + )), + + // https://ruby-doc.org/core-2.5.0/doc/syntax/literals_rdoc.html#label-Strings + escape_sequence: _ => token(seq( + '\\', + choice( + /[^ux0-7]/, // single character + /x[0-9a-fA-F]{1,2}/, // hex code + /[0-7]{1,3}/, // octal + /u[0-9a-fA-F]{4}/, // single unicode + /u\{[0-9a-fA-F ]+\}/, // multiple unicode + ), + )), + + array: $ => seq( + '[', + optional($._argument_list_with_trailing_comma), + ']', + ), + + hash: $ => seq( + '{', + optional(seq( + commaSep1(choice($.pair, $.hash_splat_argument)), + optional(','), + )), + '}', + ), + + pair: $ => prec.right(choice( + seq( + field('key', $._arg), + '=>', + field('value', $._arg), + ), + seq( + field('key', choice( + $.string, + )), + token.immediate(':'), + field('value', $._arg), + ), + seq( + field('key', choice( + $.hash_key_symbol, + alias($.identifier, $.hash_key_symbol), + alias($.constant, $.hash_key_symbol), + alias($.identifier_suffix, $.hash_key_symbol), + alias($.constant_suffix, $.hash_key_symbol), + )), + token.immediate(':'), + choice( + field('value', optional($._arg)), + // This alternative never matches, because '_no_line_break' tokens do not exist. + // The purpose is give a hint to the scanner that it should not produce any line-break + // terminators at this point. + $._no_line_break), + ), + )), + + lambda: $ => seq( + '->', + field('parameters', optional(choice( + alias($.parameters, $.lambda_parameters), + alias($.bare_parameters, $.lambda_parameters), + ))), + field('body', choice($.block, $.do_block)), + ), + + empty_statement: _ => prec(-1, ';'), + + _terminator: $ => choice( + $._line_break, + ';', + ), + }, +}); + +/** + * Creates a rule to optionally match one or more of the rules separated by `separator` + * + * @param {RuleOrLiteral} rule + * + * @param {RuleOrLiteral} separator + * + * @return {ChoiceRule} + * + */ +function sep(rule, separator) { + return optional(sep1(rule, separator)); +} + +/** + * Creates a rule to match one or more of the rules separated by `separator` + * + * @param {RuleOrLiteral} rule + * + * @param {RuleOrLiteral} separator + * + * @return {SeqRule} + * + */ +function sep1(rule, separator) { + return seq(rule, repeat(seq(separator, rule))); +} + +/** + * Creates a rule to match one or more of the rules separated by a comma + * + * @param {RuleOrLiteral} rule + * + * @return {SeqRule} + * + */ +function commaSep1(rule) { + return sep1(rule, ','); +} + +/** + * Creates a rule to optionally match one or more of the rules separated by a comma + * + * @param {RuleOrLiteral} rule + * + * @return {ChoiceRule} + * + */ +function commaSep(rule) { + return optional(commaSep1(rule)); +}