feat: sway programming language

This commit is contained in:
Riley Bruins 2024-11-01 13:34:02 -07:00 committed by Christian Clason
parent d3d4c1411c
commit 8fadb18430
9 changed files with 848 additions and 0 deletions

View file

@ -746,6 +746,9 @@
"svelte": {
"revision": "ae5199db47757f785e43a14b332118a5474de1a2"
},
"sway": {
"revision": "03d97aad336ecc6b302f23bdd9b695ddc937160b"
},
"swift": {
"revision": "5098007f58f4663a5613b2fecb6b866e3d41e37b"
},

View file

@ -2164,6 +2164,14 @@ list.svelte = {
maintainers = { "@amaanq" },
}
list.sway = {
install_info = {
url = "https://github.com/FuelLabs/tree-sitter-sway.git",
files = { "src/parser.c", "src/scanner.c" },
},
maintainers = { "@ribru17" },
}
list.swift = {
install_info = {
url = "https://github.com/alex-pinkus/tree-sitter-swift",

20
queries/sway/folds.scm Normal file
View file

@ -0,0 +1,20 @@
[
(mod_item)
(function_item)
(struct_item)
(trait_item)
(enum_item)
(impl_item)
(type_item)
(const_item)
(let_declaration)
(for_expression)
(while_expression)
(if_expression)
(match_expression)
(call_expression)
(array_expression)
(attribute_item)
(block)
(use_declaration)+
] @fold

336
queries/sway/highlights.scm Normal file
View file

@ -0,0 +1,336 @@
(type_identifier) @type
(identifier) @variable
(field_identifier) @variable.member
(escape_sequence) @string.escape
(primitive_type) @type.builtin
(boolean_literal) @boolean
(integer_literal) @number
(float_literal) @number.float
(char_literal) @character
; -------
; Paths
; -------
(use_declaration
argument: (identifier) @module)
(use_wildcard
(identifier) @module)
(mod_item
name: (identifier) @module)
(scoped_use_list
path: (identifier)? @module)
(use_list
(identifier) @module)
(use_as_clause
path: (identifier)? @module
alias: (identifier) @module)
; ---
; Remaining Paths
; ---
(scoped_identifier
path: (identifier)? @module
name: (identifier) @module)
(scoped_type_identifier
path: (identifier) @module)
[
"*"
"'"
"->"
"=>"
"<="
"="
"=="
"!"
"!="
"%"
"%="
"&"
"&="
"&&"
"|"
"|="
"||"
"^"
"^="
"*"
"*="
"-"
"-="
"+"
"+="
"/"
"/="
">"
"<"
">="
">>"
"<<"
">>="
"<<="
"@"
".."
"..="
"'"
"?"
] @operator
(use_wildcard
"*" @character.special)
[
(string_literal)
(raw_string_literal)
] @string
[
(line_comment)
(block_comment)
] @comment
; ---
; Extraneous
; ---
(self) @variable.builtin
(enum_variant
(identifier) @constant)
(field_initializer
(field_identifier) @variable.member)
(shorthand_field_initializer
(identifier) @variable.member)
(shorthand_field_identifier) @variable.member
(loop_label
"'" @label
(identifier) @label)
; ---
; Punctuation
; ---
[
"::"
":"
"."
";"
","
] @punctuation.delimiter
[
"("
")"
"["
"]"
"{"
"}"
"#"
] @punctuation.bracket
(type_arguments
[
"<"
">"
] @punctuation.bracket)
(type_parameters
[
"<"
">"
] @punctuation.bracket)
(closure_parameters
"|" @punctuation.bracket)
(let_declaration
pattern: [
(identifier) @variable
(tuple_pattern
(identifier) @variable)
])
; It needs to be anonymous to not conflict with `call_expression` further below.
(_
value: (field_expression
value: (identifier)? @variable
field: (field_identifier) @variable.member))
(parameter
pattern: (identifier) @variable.parameter)
(parameter
pattern: (ref_pattern
[
(mut_pattern
(identifier) @variable.parameter)
(identifier) @variable.parameter
]))
(closure_parameters
(identifier) @variable.parameter)
(for_expression
"for" @keyword.repeat)
"in" @keyword.repeat
[
"match"
"if"
"else"
] @keyword.conditional
"while" @keyword.repeat
[
"break"
"continue"
"return"
"yield"
] @keyword.return
"use" @keyword.import
(mod_item
"mod" @keyword.import
!body)
(use_as_clause
"as" @keyword.import)
(type_cast_expression
"as" @keyword.operator)
[
"as"
"mod"
"abi"
"impl"
"where"
"trait"
"for"
"let"
"contract"
"script"
"predicate"
"library"
] @keyword
[
"struct"
"enum"
"storage"
"configurable"
"type"
] @keyword.type
[
"fn"
"abi"
] @keyword.function
[
(mutable_specifier)
"const"
"ref"
"deref"
"move"
"pub"
] @keyword.modifier
(reference_type
"&" @keyword.modifier)
(self_parameter
"&" @keyword.modifier)
; -------
; Guess Other Types
; -------
((identifier) @constant
(#lua-match? @constant "^[A-Z][A-Z%d_]*$"))
; ---
; PascalCase identifiers in call_expressions (e.g. `Ok()`)
; are assumed to be enum constructors.
; ---
(call_expression
function: [
((identifier) @constant
(#lua-match? @constant "^[A-Z]"))
(scoped_identifier
name: ((identifier) @constant
(#lua-match? @constant "^[A-Z]")))
])
; ---
; Assume that types in match arms are enums and not
; tuple structs. Same for `if let` expressions.
; ---
(match_pattern
(scoped_identifier
name: (identifier) @constructor))
(tuple_struct_pattern
type: [
(identifier) @constructor
(scoped_identifier
name: (identifier) @constructor)
])
(struct_pattern
type: [
(type_identifier) @constructor
(scoped_type_identifier
name: (type_identifier) @constructor)
])
; ---
; Other PascalCase identifiers are assumed to be structs.
; ---
((identifier) @type
(#lua-match? @type "^[A-Z]"))
; -------
; Functions
; -------
(call_expression
function: [
(identifier) @function.call
(scoped_identifier
name: (identifier) @function.call)
(field_expression
field: (field_identifier) @function.method.call)
])
(generic_function
function: [
(identifier) @function.call
(scoped_identifier
name: (identifier) @function.call)
(field_expression
field: (field_identifier) @function.method.call)
])
(function_item
name: (identifier) @function)
(function_signature_item
name: (identifier) @function)

95
queries/sway/indents.scm Normal file
View file

@ -0,0 +1,95 @@
[
(mod_item)
(struct_item)
(enum_item)
(impl_item)
(struct_expression)
(struct_pattern)
(tuple_struct_pattern)
(tuple_expression)
(tuple_type)
(tuple_pattern)
(match_block)
(call_expression)
(asm_block)
(asm_parameters)
(assignment_expression)
(arguments)
(block)
(where_clause)
(use_list)
(array_expression)
(ordered_field_declaration_list)
(field_declaration_list)
(enum_variant_list)
(parameters)
(token_tree)
] @indent.begin
(trait_item
body: (_) @indent.begin)
(string_literal
(escape_sequence)) @indent.begin
(block
"}" @indent.end)
(asm_block
"}" @indent.end)
(enum_item
body: (enum_variant_list
"}" @indent.end))
(impl_item
body: (declaration_list
"}" @indent.end))
(match_expression
body: (match_block
"}" @indent.end))
(struct_item
body: (field_declaration_list
"}" @indent.end))
(struct_expression
body: (field_initializer_list
"}" @indent.end))
(struct_pattern
"}" @indent.end)
(tuple_struct_pattern
")" @indent.end)
(tuple_type
")" @indent.end)
(tuple_pattern
")" @indent.end)
(trait_item
body: (declaration_list
"}" @indent.end))
(impl_item
(where_clause) @indent.dedent)
[
"where"
")"
"]"
"}"
] @indent.branch
(impl_item
(declaration_list) @indent.branch)
[
(line_comment)
(string_literal)
] @indent.ignore
(raw_string_literal) @indent.auto

View file

@ -0,0 +1,5 @@
([
(line_comment)
(block_comment)
] @injection.content
(#set! injection.language "comment"))

16
queries/sway/locals.scm Normal file
View file

@ -0,0 +1,16 @@
; Scopes
[
(function_item)
(closure_expression)
(block)
] @local.scope
; Definitions
(parameter
(identifier) @local.definition)
(closure_parameters
(identifier) @local.definition)
; References
(identifier) @local.reference

345
tests/indent/sway/main.sw Normal file
View file

@ -0,0 +1,345 @@
library;
use ::alloc::{alloc, realloc};
use ::assert::assert;
use ::option::Option::{self, *};
use ::convert::From;
use ::iterator::*;
struct RawVec<T> {
ptr: raw_ptr,
cap: u64,
}
pub fn tx_witness_data<T>(index: u64) -> Option<T> {
if index >= tx_witnesses_count() {
return None
}
let length = match tx_witness_data_length(index) {
Some(len) => len,
None => return None,
};
if __is_reference_type::<T>() {
let witness_data_ptr = __gtf::<raw_ptr>(index, GTF_WITNESS_DATA);
let new_ptr = alloc_bytes(length);
witness_data_ptr.copy_bytes_to(new_ptr, length);
Some(asm(ptr: new_ptr) {
ptr: T
})
} else {
// u8 is the only value type that is less than 8 bytes and should be handled separately
if __size_of::<T>() == 1 {
Some(__gtf::<raw_ptr>(index, GTF_WITNESS_DATA).add::<u8>(7).read::<T>())
} else {
Some(__gtf::<raw_ptr>(index, GTF_WITNESS_DATA).read::<T>())
}
}
}
impl<T> RawVec<T> {
pub fn new() -> Self {
Self {
ptr: alloc::<T>(0),
cap: 0,
}
}
pub fn with_capacity(capacity: u64) -> Self {
Self {
ptr: alloc::<T>(capacity),
cap: capacity,
}
}
pub fn ptr(self) -> raw_ptr {
self.ptr
}
pub fn capacity(self) -> u64 {
self::cap()
}
pub fn grow(ref mut self) {
let new_cap = if self.cap == 0 { 1 } else { 2 * self.cap };
self.ptr = realloc::<T>(self.ptr, self.cap, new_cap);
self.cap = new_cap;
}
}
impl<T> From<raw_slice> for RawVec<T> {
fn from(slice: raw_slice) -> Self {
let cap = slice.len::<T>();
let ptr = alloc::<T>(cap);
if cap > 0 {
slice.ptr().copy_to::<T>(ptr, cap);
}
Self { ptr, cap }
}
}
pub struct Vec<T> {
buf: RawVec<T>,
len: u64,
}
impl<T> Vec<T> {
pub fn new() -> Self {
'hey: while true {
}
Self {
buf: RawVec::new(),
len: 0,
}
}
pub fn with_capacity(capacity: u64) -> Self {
Self {
buf: RawVec::with_capacity(capacity),
len: 0,
}
}
pub fn push(ref mut self, value: T) {
// If there is insufficient capacity, grow the buffer.
if self.len == self.buf.capacity() {
self.buf.grow();
};
// Get a pointer to the end of the buffer, where the new element will
// be inserted.
let end = self.buf.ptr().add::<T>(self.len);
// Write `value` at pointer `end`
end.write::<T>(value);
// Increment length.
self.len += 1;
}
pub fn capacity(self) -> u64 {
self.buf.capacity()
}
pub fn clear(ref mut self) {
self.len = 0;
}
pub fn get(self, index: u64) -> Option<T> {
// First check that index is within bounds.
if self.len <= index {
return None;
};
// Get a pointer to the desired element using `index`
let ptr = self.buf.ptr().add::<T>(index);
// Read from `ptr`
Some(ptr.read::<T>())
}
pub fn len(self) -> u64 {
self.len
}
pub fn is_empty(self) -> bool {
self.len == 0
}
pub fn remove(ref mut self, index: u64) -> T {
assert(index < self.len);
let buf_start = self.buf.ptr();
// Read the value at `index`
let ptr = buf_start.add::<T>(index);
let ret = ptr.read::<T>();
// Shift everything down to fill in that spot.
let mut i = index;
if self.len > 1 {
while i < self.len - 1 {
let ptr = buf_start.add::<T>(i);
ptr.add::<T>(1).copy_to::<T>(ptr, 1);
i += 1;
}
}
// Decrease length.
self.len -= 1;
ret
}
pub fn insert(ref mut self, index: u64, element: T) {
assert(index <= self.len);
// If there is insufficient capacity, grow the buffer.
if self.len == self.buf.capacity() {
self.buf.grow();
}
let buf_start = self.buf.ptr();
// The spot to put the new value
let index_ptr = buf_start.add::<T>(index);
// Shift everything over to make space.
let mut i = self.len;
while i > index {
let ptr = buf_start.add::<T>(i);
ptr.sub::<T>(1).copy_to::<T>(ptr, 1);
i -= 1;
}
// Write `element` at pointer `index`
index_ptr.write::<T>(element);
// Increment length.
self.len += 1;
}
pub fn pop(ref mut self) -> Option<T> {
if self.len == 0 {
return None;
}
self.len -= 1;
Some(self.buf.ptr().add::<T>(self.len).read::<T>())
}
pub fn swap(ref mut self, element1_index: u64, element2_index: u64) {
assert(element1_index < self.len);
assert(element2_index < self.len);
if element1_index == element2_index {
return;
}
let element1_ptr = self.buf.ptr().add::<T>(element1_index);
let element2_ptr = self.buf.ptr().add::<T>(element2_index);
let element1_val: T = element1_ptr.read::<T>();
element2_ptr.copy_to::<T>(element1_ptr, 1);
element2_ptr.write::<T>(element1_val);
}
pub fn set(ref mut self, index: u64, value: T) {
assert(index < self.len);
let index_ptr = self.buf.ptr().add::<T>(index);
index_ptr.write::<T>(value);
}
pub fn iter(self) -> VecIter<T> {
VecIter {
values: self,
index: 0,
}
}
pub fn ptr(self) -> raw_ptr {
self.buf.ptr()
}
}
impl<T> AsRawSlice for Vec<T> {
fn as_raw_slice(self) -> raw_slice {
raw_slice::from_parts::<T>(self.buf.ptr(), self.len)
}
}
impl<T> From<raw_slice> for Vec<T> {
fn from(slice: raw_slice) -> Self {
Self {
buf: RawVec::from(slice),
len: slice.len::<T>(),
}
}
}
impl<T> From<Vec<T>> for raw_slice {
fn from(vec: Vec<T>) -> Self {
asm(ptr: (vec.ptr(), vec.len())) {
ptr: raw_slice
}
}
pub fn sha256(self) -> b256 {
let mut result_buffer = b256::min();
asm(
hash: result_buffer,
ptr: p,
bytes: p,
) {
s256 hash ptr bytes;
hash: b256
}
}
}
impl<T> AbiEncode for Vec<T>
where
T: AbiEncode,
{
fn abi_encode(self, buffer: Buffer) -> Buffer {
let len = self.len();
let mut buffer = len.abi_encode(buffer);
let mut i = 0;
while i < len {
let item = self.get(i).unwrap();
buffer = item.abi_encode(buffer);
i += 1;
}
buffer
}
}
impl<T> AbiDecode for Vec<T>
where
T: AbiDecode,
{
fn abi_decode(ref buffer: BufferReader) -> Vec<T> {
let len = u64::abi_decode(buffer);
let mut v = Vec::with_capacity(len);
let mut i = 0;
while i < len {
let item = T::abi_decode(buffer);
v.push(item);
i += 1;
}
yield 5;
v
}
}
pub struct VecIter<T> {
values: Vec<T>,
index: u64,
}
impl<T> Iterator for VecIter<T> {
type Item = T;
fn next(ref mut self) -> Option<Self::Item> {
if self.index >= self.values.len()? {
return None
}
self.index += 1;
self.values.get(self.index - 1)
}
}
fn hello<T>(hi: T) {
println("{}", hi);
}
// vim: ft=sway

View file

@ -0,0 +1,20 @@
local Runner = require("tests.indent.common").Runner
local run = Runner:new(it, "tests/indent/sway", {
tabstop = 4,
shiftwidth = 4,
softtabstop = 4,
expandtab = true,
})
describe("indent Sway:", function()
describe("whole file:", function()
run:whole_file(".", {})
end)
describe("new line:", function()
run:new_line("main.sw", { on_line = 12, text = "const CONST: u32 = 2;", indent = 0 })
run:new_line("main.sw", { on_line = 14, text = "let hi = 5;", indent = 4 })
run:new_line("main.sw", { on_line = 15, text = "let hi = 5;", indent = 8 })
run:new_line("main.sw", { on_line = 92, text = "let hi = 5;", indent = 12 })
end)
end)