Skip to content

Commit

Permalink
Adding Move On Sui Semgrep grammer (#508)
Browse files Browse the repository at this point in the history
* First instance of adding semgrep grammars for Move-on-Sui language.

Currently everything in the tree-sitter grammer is fully supported and all tests pass.

However, several things are missing from the tree-sitter grammar today:
- Proper support for annotations
- Support for scripts
- The most recent changes to the Move language

* Moving Move on Sui tree-sitter to my branch for accelerated developement

* Added a package to stats, this has a lot of tests that are updated with move as this is a vscode highigher

* Fixing Macro parsing bug due to meta variables eating the $ and 1 only upcase chars

* updating tree-sitter-move-on-sui repo

* Fixing Macros so now macro variables that start with $ parse.

* Simplifying _identifier_or_metavariable

* Adding ellipsis to module_access
  • Loading branch information
maxmysten authored Sep 17, 2024
1 parent 73bf698 commit d14c9ac
Show file tree
Hide file tree
Showing 71 changed files with 9,097 additions and 9 deletions.
2 changes: 1 addition & 1 deletion .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -123,4 +123,4 @@
url = https://github.com/Decurity/tree-sitter-circom.git
[submodule "lang/semgrep-grammars/src/tree-sitter-move-on-sui"]
path = lang/semgrep-grammars/src/tree-sitter-move-on-sui
url = https://github.com/tzakian/tree-sitter-move.git
url = https://github.com/maxmysten/tree-sitter-move.git
3 changes: 2 additions & 1 deletion lang/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,8 @@ STAT_LANGUAGES2 = \
rust \
solidity \
tsx \
typescript
typescript \
move-on-sui

STAT_LANGUAGES = $(STAT_LANGUAGES1) $(STAT_LANGUAGES2)

Expand Down
1 change: 1 addition & 0 deletions lang/move-on-sui/projects.txt
Original file line number Diff line number Diff line change
Expand Up @@ -4,3 +4,4 @@
https://github.com/AftermathFinance/move-interfaces
https://github.com/Bucket-Protocol/bucket-interface
https://github.com/MystenLabs/sui-axelar
https://github.com/MystenLabs/prettier-move/
79 changes: 79 additions & 0 deletions lang/move-on-sui/test/ok/address.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

module sui::address {
use sui::hex;
use std::ascii;
use std::bcs;
use std::string;

/// The length of an address, in bytes
const LENGTH: u64 = 32;

// The largest integer that can be represented with 32 bytes: 2^(8*32) - 1
const MAX: u256 = 115792089237316195423570985008687907853269984665640564039457584007913129639935;

#[allow(unused_const)]
/// Error from `from_bytes` when it is supplied too many or too few bytes.
const EAddressParseError: u64 = 0;

/// Convert `a` into a u256 by interpreting `a` as the bytes of a big-endian integer
/// (e.g., `to_u256(0x1) == 1`)
public native fun to_u256(a: address): u256;

spec to_u256 {
pragma opaque;
// TODO: stub to be replaced by actual abort conditions if any
aborts_if [abstract] true;
// TODO: specify actual function behavior
}

/// Convert `n` into an address by encoding it as a big-endian integer (e.g., `from_u256(1) = @0x1`)
/// Aborts if `n` > `MAX_ADDRESS`
public native fun from_u256(n: u256): address;

spec from_u256 {
pragma opaque;
// TODO: stub to be replaced by actual abort conditions if any
aborts_if [abstract] true;
// TODO: specify actual function behavior
}

/// Convert `bytes` into an address.
/// Aborts with `EAddressParseError` if the length of `bytes` is not 32
public native fun from_bytes(bytes: vector<u8>): address;

spec from_bytes {
pragma opaque;
// TODO: stub to be replaced by actual abort conditions if any
aborts_if [abstract] true;
// TODO: specify actual function behavior
}

/// Convert `a` into BCS-encoded bytes.
public fun to_bytes(a: address): vector<u8> {
bcs::to_bytes(&a)
}

/// Convert `a` to a hex-encoded ASCII string
public fun to_ascii_string(a: address): ascii::String {
ascii::string(hex::encode(to_bytes(a)))
}

/// Convert `a` to a hex-encoded ASCII string
public fun to_string(a: address): string::String {
string::from_ascii(to_ascii_string(a))
}

/// Length of a Sui address in bytes
public fun length(): u64 {
LENGTH
}

/// Largest possible address
public fun max(): u256 {
MAX
}

spec module { pragma verify = false; }
}
9 changes: 9 additions & 0 deletions lang/move-on-sui/test/ok/annotations.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#[allow(unused_const, unused_variable)]

#[allow(unused_const, unused_variable)]
#[test, expected_failure(abort_code = ::staking::staking_tests::ENotImplemented)]
#[test, expected_failure(abort_code = other_module::ENotFound)]
#[expected_failure(arithmetic_error, location = pkg_addr::other_module)]

// #[allow(unused_const, unused_variable)]
module c::k {}
71 changes: 71 additions & 0 deletions lang/move-on-sui/test/ok/as_expressions.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
module foo::bar {
fun f(): u64 {
1 as u64;
1 + 2 as u64;
(1 + 2) as u64;
1 + (2 as u64);
v[1 as u64];
1 as u64 + 2;
(1 as u64) + 2;
}

fun simple(cond: bool, x: u32) {
(if (cond) x else { x }) as u32;
(if (cond) x else { x } as u32);
(loop { break 0 }) as u32;
(loop { break 0 } as u32);
('l: { 0 }) as u32;
('l: { 0 } as u32);
(return) as u32;
(return as u32);
loop {
(break) as u32;
(break as u32);
(continue) as u32;
(continue as u32);
};
(0 as u32) as u32;
(0 as u32 as u32);
}

public fun f_imm(s: &S): &u64 { &s.f }

fun ops(x: u8, y: u8) {
(x + y as u32);
(x - y as u32);
(x * y as u32);
(x / y as u32);
(x % y as u32);
(x & y as u32);
(x | y as u32);
(x ^ y as u32);
(x << y as u32);
(x >> y as u32);

(x + y) as u32;
(x - y) as u32;
(x * y) as u32;
(x / y) as u32;
(x % y) as u32;
(x & y) as u32;
(x | y) as u32;
(x ^ y) as u32;
(x << y) as u32;
(x >> y) as u32;
}


public struct S has copy, drop { f: u64 }

fun dotted(cond: bool, mut s: S) {
(1 + s.f) as u32;
(1 + s.f as u32);
(1 + S { f: 0 }.f) as u32;
(1 + S { f: 0 }.f as u32);
(*if (cond) { &0 } else { &mut 0 }) as u32;
// this case still does not work
// (*if (cond) { &0 } else { &mut 0 } as u32);
(*if (cond) { &s } else {&mut s}.f_imm()) as u32;
(*if (cond) { &s } else {&mut s}.f_imm() as u32);
}
}
151 changes: 151 additions & 0 deletions lang/move-on-sui/test/ok/ascii.move
Original file line number Diff line number Diff line change
@@ -0,0 +1,151 @@
// Copyright (c) Mysten Labs, Inc.
// SPDX-License-Identifier: Apache-2.0

/// The `ASCII` module defines basic string and char newtypes in Move that verify
/// that characters are valid ASCII, and that strings consist of only valid ASCII characters.
module std::ascii {
use std::vector;
use std::option::{Self, Option};

/// An invalid ASCII character was encountered when creating an ASCII string.
const EINVALID_ASCII_CHARACTER: u64 = 0x10000;

/// The `String` struct holds a vector of bytes that all represent
/// valid ASCII characters. Note that these ASCII characters may not all
/// be printable. To determine if a `String` contains only "printable"
/// characters you should use the `all_characters_printable` predicate
/// defined in this module.
struct String has copy, drop, store {
bytes: vector<u8>,
}
spec String {
invariant forall i in 0..len(bytes): is_valid_char(bytes[i]);
}

/// An ASCII character.
struct Char has copy, drop, store {
byte: u8,
}
spec Char {
invariant is_valid_char(byte);
}

/// Convert a `byte` into a `Char` that is checked to make sure it is valid ASCII.
public fun char(byte: u8): Char {
assert!(is_valid_char(byte), EINVALID_ASCII_CHARACTER);
Char { byte }
}
spec char {
aborts_if !is_valid_char(byte) with EINVALID_ASCII_CHARACTER;
}

/// Convert a vector of bytes `bytes` into an `String`. Aborts if
/// `bytes` contains non-ASCII characters.
public fun string(bytes: vector<u8>): String {
let x = try_string(bytes);
assert!(
option::is_some(&x),
EINVALID_ASCII_CHARACTER
);
option::destroy_some(x)
}
spec string {
aborts_if exists i in 0..len(bytes): !is_valid_char(bytes[i]) with EINVALID_ASCII_CHARACTER;
}

/// Convert a vector of bytes `bytes` into an `String`. Returns
/// `Some(<ascii_string>)` if the `bytes` contains all valid ASCII
/// characters. Otherwise returns `None`.
public fun try_string(bytes: vector<u8>): Option<String> {
let len = vector::length(&bytes);
let i = 0;
while ({
spec {
invariant i <= len;
invariant forall j in 0..i: is_valid_char(bytes[j]);
};
i < len
}) {
let possible_byte = *vector::borrow(&bytes, i);
if (!is_valid_char(possible_byte)) return option::none();
i = i + 1;
};
spec {
assert i == len;
assert forall j in 0..len: is_valid_char(bytes[j]);
};
option::some(String { bytes })
}

/// Returns `true` if all characters in `string` are printable characters
/// Returns `false` otherwise. Not all `String`s are printable strings.
public fun all_characters_printable(string: &String): bool {
let len = vector::length(&string.bytes);
let i = 0;
while ({
spec {
invariant i <= len;
invariant forall j in 0..i: is_printable_char(string.bytes[j]);
};
i < len
}) {
let byte = *vector::borrow(&string.bytes, i);
if (!is_printable_char(byte)) return false;
i = i + 1;
};
spec {
assert i == len;
assert forall j in 0..len: is_printable_char(string.bytes[j]);
};
true
}
spec all_characters_printable {
ensures result ==> (forall j in 0..len(string.bytes): is_printable_char(string.bytes[j]));
}

public fun push_char(string: &mut String, char: Char) {
vector::push_back(&mut string.bytes, char.byte);
}
spec push_char {
ensures len(string.bytes) == len(old(string.bytes)) + 1;
}

public fun pop_char(string: &mut String): Char {
Char { byte: vector::pop_back(&mut string.bytes) }
}
spec pop_char {
ensures len(string.bytes) == len(old(string.bytes)) - 1;
}

public fun length(string: &String): u64 {
vector::length(as_bytes(string))
}

/// Get the inner bytes of the `string` as a reference
public fun as_bytes(string: &String): &vector<u8> {
&string.bytes
}

/// Unpack the `string` to get its backing bytes
public fun into_bytes(string: String): vector<u8> {
let String { bytes } = string;
bytes
}

/// Unpack the `char` into its underlying byte.
public fun byte(char: Char): u8 {
let Char { byte } = char;
byte
}

/// Returns `true` if `b` is a valid ASCII character. Returns `false` otherwise.
public fun is_valid_char(b: u8): bool {
b <= 0x7F
}

/// Returns `true` if `byte` is an printable ASCII character. Returns `false` otherwise.
public fun is_printable_char(byte: u8): bool {
byte >= 0x20 && // Disallow metacharacters
byte <= 0x7E // Don't allow DEL metacharacter
}
}
Loading

0 comments on commit d14c9ac

Please sign in to comment.