first commit
This commit is contained in:
commit
4ff159dd4a
20 changed files with 6529 additions and 0 deletions
1
.gitignore
vendored
Normal file
1
.gitignore
vendored
Normal file
|
|
@ -0,0 +1 @@
|
||||||
|
/target
|
||||||
303
Cargo.lock
generated
Normal file
303
Cargo.lock
generated
Normal file
|
|
@ -0,0 +1,303 @@
|
||||||
|
# This file is automatically @generated by Cargo.
|
||||||
|
# It is not intended for manual editing.
|
||||||
|
version = 4
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "aho-corasick"
|
||||||
|
version = "1.1.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301"
|
||||||
|
dependencies = [
|
||||||
|
"memchr",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstream"
|
||||||
|
version = "0.6.21"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"anstyle-parse",
|
||||||
|
"anstyle-query",
|
||||||
|
"anstyle-wincon",
|
||||||
|
"colorchoice",
|
||||||
|
"is_terminal_polyfill",
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle"
|
||||||
|
version = "1.0.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-parse"
|
||||||
|
version = "0.2.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2"
|
||||||
|
dependencies = [
|
||||||
|
"utf8parse",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-query"
|
||||||
|
version = "1.1.5"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "anstyle-wincon"
|
||||||
|
version = "3.0.11"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d"
|
||||||
|
dependencies = [
|
||||||
|
"anstyle",
|
||||||
|
"once_cell_polyfill",
|
||||||
|
"windows-sys",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "beef"
|
||||||
|
version = "0.5.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3a8241f3ebb85c056b509d4327ad0358fbbba6ffb340bf388f26350aeda225b1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap"
|
||||||
|
version = "4.5.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "c9e340e012a1bf4935f5282ed1436d1489548e8f72308207ea5df0e23d2d03f8"
|
||||||
|
dependencies = [
|
||||||
|
"clap_builder",
|
||||||
|
"clap_derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_builder"
|
||||||
|
version = "4.5.53"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d76b5d13eaa18c901fd2f7fca939fefe3a0727a953561fefdf3b2922b8569d00"
|
||||||
|
dependencies = [
|
||||||
|
"anstream",
|
||||||
|
"anstyle",
|
||||||
|
"clap_lex",
|
||||||
|
"strsim",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_derive"
|
||||||
|
version = "4.5.49"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2a0b5487afeab2deb2ff4e03a807ad1a03ac532ff5a2cee5d86884440c7f7671"
|
||||||
|
dependencies = [
|
||||||
|
"heck",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "clap_lex"
|
||||||
|
version = "0.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a1d728cc89cf3aee9ff92b05e62b19ee65a02b5702cff7d5a377e32c6ae29d8d"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "colorchoice"
|
||||||
|
version = "1.0.4"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "equivalent"
|
||||||
|
version = "1.0.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "fnv"
|
||||||
|
version = "1.0.7"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "3f9eec918d3f24069decb9af1554cad7c880e2da24a9afd88aca000531ab82c1"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "hashbrown"
|
||||||
|
version = "0.16.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "heck"
|
||||||
|
version = "0.5.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "indexmap"
|
||||||
|
version = "2.12.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "0ad4bb2b565bca0645f4d68c5c9af97fba094e9791da685bf83cb5f3ce74acf2"
|
||||||
|
dependencies = [
|
||||||
|
"equivalent",
|
||||||
|
"hashbrown",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "is_terminal_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a790d11254054e5dc83902dba85d253ff06ceb0cfafb12be8773435cb9dfb4f4"
|
||||||
|
dependencies = [
|
||||||
|
"logos-derive",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos-codegen"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f60337c43a38313b58871f8d5d76872b8e17aa9d51fad494b5e76092c0ce05f5"
|
||||||
|
dependencies = [
|
||||||
|
"beef",
|
||||||
|
"fnv",
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"regex-automata",
|
||||||
|
"regex-syntax",
|
||||||
|
"rustc_version",
|
||||||
|
"syn",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "logos-derive"
|
||||||
|
version = "0.16.0"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d151b2ae667f69e10b8738f5cac0c746faa22b2e15ea7e83b55476afec3767dc"
|
||||||
|
dependencies = [
|
||||||
|
"logos-codegen",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "memchr"
|
||||||
|
version = "2.7.6"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f52b00d39961fc5b2736ea853c9cc86238e165017a493d1d5c8eac6bdc4cc273"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "once_cell_polyfill"
|
||||||
|
version = "1.70.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "proc-macro2"
|
||||||
|
version = "1.0.103"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5ee95bc4ef87b8d5ba32e8b7714ccc834865276eab0aed5c9958d00ec45f49e8"
|
||||||
|
dependencies = [
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "quote"
|
||||||
|
version = "1.0.42"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "a338cc41d27e6cc6dce6cefc13a0729dfbb81c262b1f519331575dd80ef3067f"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-automata"
|
||||||
|
version = "0.4.13"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "5276caf25ac86c8d810222b3dbb938e512c55c6831a10f3e6ed1c93b84041f1c"
|
||||||
|
dependencies = [
|
||||||
|
"aho-corasick",
|
||||||
|
"memchr",
|
||||||
|
"regex-syntax",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "regex-syntax"
|
||||||
|
version = "0.8.8"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7a2d987857b319362043e95f5353c0535c1f58eec5336fdfcf626430af7def58"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "rustc_version"
|
||||||
|
version = "0.4.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "cfcb3a22ef46e85b45de6ee7e79d063319ebb6594faafcf1c225ea92ab6e9b92"
|
||||||
|
dependencies = [
|
||||||
|
"semver",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "semver"
|
||||||
|
version = "1.0.27"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "d767eb0aabc880b29956c35734170f26ed551a859dbd361d140cdbeca61ab1e2"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "strsim"
|
||||||
|
version = "0.11.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "suicmez"
|
||||||
|
version = "0.1.0"
|
||||||
|
dependencies = [
|
||||||
|
"clap",
|
||||||
|
"indexmap",
|
||||||
|
"logos",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "syn"
|
||||||
|
version = "2.0.111"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "390cc9a294ab71bdb1aa2e99d13be9c753cd2d7bd6560c77118597410c4d2e87"
|
||||||
|
dependencies = [
|
||||||
|
"proc-macro2",
|
||||||
|
"quote",
|
||||||
|
"unicode-ident",
|
||||||
|
]
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "unicode-ident"
|
||||||
|
version = "1.0.22"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "9312f7c4f6ff9069b165498234ce8be658059c6728633667c526e27dc2cf1df5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "utf8parse"
|
||||||
|
version = "0.2.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-link"
|
||||||
|
version = "0.2.1"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5"
|
||||||
|
|
||||||
|
[[package]]
|
||||||
|
name = "windows-sys"
|
||||||
|
version = "0.61.2"
|
||||||
|
source = "registry+https://github.com/rust-lang/crates.io-index"
|
||||||
|
checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc"
|
||||||
|
dependencies = [
|
||||||
|
"windows-link",
|
||||||
|
]
|
||||||
9
Cargo.toml
Normal file
9
Cargo.toml
Normal file
|
|
@ -0,0 +1,9 @@
|
||||||
|
[package]
|
||||||
|
name = "suicmez"
|
||||||
|
version = "0.1.0"
|
||||||
|
edition = "2024"
|
||||||
|
|
||||||
|
[dependencies]
|
||||||
|
clap = { version = "4.5.53", features = ["derive"] }
|
||||||
|
indexmap = "2.0"
|
||||||
|
logos = "0.16.0"
|
||||||
7
simple_test.sui
Normal file
7
simple_test.sui
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
fn simple_add(x: int, y: int) -> int
|
||||||
|
x + y
|
||||||
|
|
||||||
|
fn main() -> int do
|
||||||
|
let result = simple_add(5, 3)
|
||||||
|
result
|
||||||
|
end
|
||||||
430
src/ast.rs
Normal file
430
src/ast.rs
Normal file
|
|
@ -0,0 +1,430 @@
|
||||||
|
use crate::typechecker::Type;
|
||||||
|
use std::ops::Range;
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum TypeAnnot {
|
||||||
|
Var(String),
|
||||||
|
Cons(String, Vec<TypeAnnot>),
|
||||||
|
Function(Vec<TypeAnnot>, Box<TypeAnnot>),
|
||||||
|
Tuple(Vec<TypeAnnot>),
|
||||||
|
Array(Box<TypeAnnot>),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Span {
|
||||||
|
pub start: usize,
|
||||||
|
pub end: usize,
|
||||||
|
pub file: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Span {
|
||||||
|
pub fn new(range: &Range<usize>, file: String) -> Self {
|
||||||
|
Span {
|
||||||
|
start: range.start,
|
||||||
|
end: range.end,
|
||||||
|
file,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn merge(&self, other: &Span) -> Span {
|
||||||
|
Span {
|
||||||
|
start: self.start.min(other.start),
|
||||||
|
end: self.end.max(other.end),
|
||||||
|
file: self.file.clone(),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// @attribute
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Attribute {
|
||||||
|
pub name: String,
|
||||||
|
pub args: Vec<AttributeArg>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum AttributeArg {
|
||||||
|
Value(String), // some_identifier
|
||||||
|
KeyValue(String, String), // some_key = some_identifier
|
||||||
|
Literal(String), // some literal value
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct ASTNode {
|
||||||
|
pub kind: ASTNodeKind,
|
||||||
|
pub span: Span,
|
||||||
|
pub attributes: Vec<Attribute>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ASTNodeKind {
|
||||||
|
Function(Function),
|
||||||
|
Extern(Extern),
|
||||||
|
Load(Load),
|
||||||
|
Struct(Struct),
|
||||||
|
Enum(Enum),
|
||||||
|
Impl(Impl),
|
||||||
|
Trait(Trait),
|
||||||
|
Use(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
// ? implies OPTIONAL here
|
||||||
|
// \( implies the presence of (. same for /)
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
/// fn name\( (arg: type?,)* \) -> return_type? body
|
||||||
|
pub struct Function {
|
||||||
|
pub name: String,
|
||||||
|
pub parameters: Vec<Parameter>, // type params
|
||||||
|
pub args: Vec<(String, Option<TypeAnnot>)>,
|
||||||
|
pub return_type: Option<TypeAnnot>,
|
||||||
|
pub body: Expr,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// extern name\( type?,* \) -> return_type from library_alias
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Extern {
|
||||||
|
pub name: String,
|
||||||
|
pub args: Vec<TypeAnnot>,
|
||||||
|
pub return_type: TypeAnnot,
|
||||||
|
pub from: String,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// load "library" as alias
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Load {
|
||||||
|
pub library: String,
|
||||||
|
pub alias: String,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// struct name <parameter*>?
|
||||||
|
/// (field_name: field_type,)*
|
||||||
|
/// end
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Struct {
|
||||||
|
pub name: String,
|
||||||
|
pub parameters: Vec<Parameter>, // type parameters
|
||||||
|
pub fields: Vec<Field>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Field {
|
||||||
|
pub name: String,
|
||||||
|
pub field_type: TypeAnnot,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// enum name <parameter*>?
|
||||||
|
/// VariantName\(field_type,\)*
|
||||||
|
/// end
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Enum {
|
||||||
|
pub name: String,
|
||||||
|
pub parameters: Vec<Parameter>, // type parameters
|
||||||
|
pub variants: Vec<Variant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Parameter {
|
||||||
|
pub name: String,
|
||||||
|
pub bounds: Vec<String>, // trait bounds
|
||||||
|
pub kind: Option<Kind>, // for HKTs
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Kind {
|
||||||
|
Star, // *
|
||||||
|
Arrow(Box<Kind>, Box<Kind>), // k1 -> k2
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Variant {
|
||||||
|
pub name: String,
|
||||||
|
pub fields: Vec<TypeAnnot>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// impl TypeName <parameter*>? (: TraitName)?
|
||||||
|
/// functions*
|
||||||
|
/// end
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Impl {
|
||||||
|
pub target: String,
|
||||||
|
pub trait_name: Option<String>,
|
||||||
|
pub methods: Vec<Function>,
|
||||||
|
}
|
||||||
|
|
||||||
|
/// trait TraitName <parameter*>?
|
||||||
|
/// function_signatures*
|
||||||
|
/// end
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Trait {
|
||||||
|
pub name: String,
|
||||||
|
pub methods: Vec<FunctionSignature>,
|
||||||
|
|
||||||
|
pub parameters: Vec<Parameter>,
|
||||||
|
pub associated_types: Vec<AssociatedType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct AssociatedType {
|
||||||
|
pub name: String,
|
||||||
|
pub bounds: Vec<String>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct FunctionSignature {
|
||||||
|
pub name: String,
|
||||||
|
pub params: Vec<Parameter>,
|
||||||
|
pub return_type: TypeAnnot,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Expr {
|
||||||
|
pub kind: ExprKind,
|
||||||
|
pub span: Span,
|
||||||
|
pub attributes: Vec<Attribute>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum ExprKind {
|
||||||
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
|
Bool(bool),
|
||||||
|
String(String),
|
||||||
|
Array(Vec<Expr>),
|
||||||
|
Tuple(Vec<Expr>),
|
||||||
|
|
||||||
|
StructLit(String, Vec<(String, Expr)>), // Name { a: expr, b: expr }
|
||||||
|
EnumLit(String, String, Vec<Expr>), // Name::Variant(expr, expr)
|
||||||
|
|
||||||
|
Variable(String),
|
||||||
|
|
||||||
|
Call(Box<Expr>, Vec<Expr>),
|
||||||
|
Index(Box<Expr>, Box<Expr>),
|
||||||
|
Dot(Box<Expr>, String),
|
||||||
|
EarlyReturn(Option<Box<Expr>>), // eg: myresultoroption?
|
||||||
|
OptionalChain(Option<Box<Expr>>, String), // a?.b
|
||||||
|
|
||||||
|
Lambda(Vec<(String, Option<TypeAnnot>)>, Box<Expr>), // lambda (arg, arg: optionalty, ...) body
|
||||||
|
Let(String, BindingKind, Option<TypeAnnot>, Box<Expr>), // no patterns for now
|
||||||
|
Assign(Box<Expr>, Box<Expr>), // NOTE: check for valid lvalue during typechecking
|
||||||
|
Cast(Box<Expr>, TypeAnnot),
|
||||||
|
|
||||||
|
If(Box<Expr>, Box<Expr>, Option<Box<Expr>>), // if cond expr (else expr)?
|
||||||
|
Match(Box<Expr>, Vec<(Pattern, Expr)>), // match expr pattern => expr* end
|
||||||
|
While(Box<Expr>, Box<Expr>), // while cond expr
|
||||||
|
|
||||||
|
For(String, Box<Expr>, Box<Expr>), // for i in expr body
|
||||||
|
Range(Box<Expr>, Box<Expr>), // 0..10
|
||||||
|
|
||||||
|
Do(Vec<Expr>), // do expr* end
|
||||||
|
BinOp(Box<Expr>, BinOp, Box<Expr>),
|
||||||
|
UnOp(UnOp, Box<Expr>),
|
||||||
|
|
||||||
|
Return(Option<Box<Expr>>),
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum BindingKind {
|
||||||
|
Default, // immutable but infinite usages
|
||||||
|
Mutable, // mutable but infinite usages
|
||||||
|
Affine,
|
||||||
|
Linear,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum BinOp {
|
||||||
|
Add,
|
||||||
|
Sub,
|
||||||
|
Mul,
|
||||||
|
Div,
|
||||||
|
Mod,
|
||||||
|
And,
|
||||||
|
Or,
|
||||||
|
Eq,
|
||||||
|
Neq,
|
||||||
|
Lt,
|
||||||
|
Gt,
|
||||||
|
Leq,
|
||||||
|
Geq,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum UnOp {
|
||||||
|
Neg,
|
||||||
|
Not,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct Pattern {
|
||||||
|
pub kind: PatternKind,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum PatternKind {
|
||||||
|
Wildcard, // _
|
||||||
|
Variable(String),
|
||||||
|
Literal(String),
|
||||||
|
Tuple(Vec<Pattern>),
|
||||||
|
Struct(String, Vec<(String, Pattern)>),
|
||||||
|
Enum(String, String, Vec<Pattern>),
|
||||||
|
Range(i64, i64),
|
||||||
|
}
|
||||||
|
|
||||||
|
// Typed variants
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedASTNode {
|
||||||
|
pub kind: TypedASTNodeKind,
|
||||||
|
pub span: Span,
|
||||||
|
pub attributes: Vec<Attribute>,
|
||||||
|
pub ty: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum TypedASTNodeKind {
|
||||||
|
Function(TypedFunction),
|
||||||
|
Extern(TypedExtern),
|
||||||
|
Load(TypedLoad),
|
||||||
|
Struct(TypedStruct),
|
||||||
|
Enum(TypedEnum),
|
||||||
|
Impl(TypedImpl),
|
||||||
|
Trait(TypedTrait),
|
||||||
|
Use(String),
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedFunction {
|
||||||
|
pub name: String,
|
||||||
|
pub parameters: Vec<Parameter>,
|
||||||
|
pub args: Vec<(String, Option<TypeAnnot>)>,
|
||||||
|
pub return_type: Option<TypeAnnot>,
|
||||||
|
pub body: TypedExpr,
|
||||||
|
pub ty: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedExtern {
|
||||||
|
pub name: String,
|
||||||
|
pub args: Vec<TypeAnnot>,
|
||||||
|
pub return_type: TypeAnnot,
|
||||||
|
pub from: String,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedLoad {
|
||||||
|
pub library: String,
|
||||||
|
pub alias: String,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedStruct {
|
||||||
|
pub name: String,
|
||||||
|
pub parameters: Vec<Parameter>,
|
||||||
|
pub fields: Vec<TypedField>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedField {
|
||||||
|
pub name: String,
|
||||||
|
pub field_type: TypeAnnot,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedEnum {
|
||||||
|
pub name: String,
|
||||||
|
pub parameters: Vec<Parameter>,
|
||||||
|
pub variants: Vec<TypedVariant>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedVariant {
|
||||||
|
pub name: String,
|
||||||
|
pub fields: Vec<TypeAnnot>,
|
||||||
|
pub span: Span,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedImpl {
|
||||||
|
pub target: String,
|
||||||
|
pub trait_name: Option<String>,
|
||||||
|
pub methods: Vec<TypedFunction>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedTrait {
|
||||||
|
pub name: String,
|
||||||
|
pub methods: Vec<FunctionSignature>,
|
||||||
|
pub parameters: Vec<Parameter>,
|
||||||
|
pub associated_types: Vec<AssociatedType>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedExpr {
|
||||||
|
pub kind: TypedExprKind,
|
||||||
|
pub span: Span,
|
||||||
|
pub attributes: Vec<Attribute>,
|
||||||
|
pub ty: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum TypedExprKind {
|
||||||
|
Int(i64),
|
||||||
|
Float(f64),
|
||||||
|
Bool(bool),
|
||||||
|
String(String),
|
||||||
|
Array(Vec<TypedExpr>),
|
||||||
|
Tuple(Vec<TypedExpr>),
|
||||||
|
StructLit(String, Vec<(String, TypedExpr)>),
|
||||||
|
EnumLit(String, String, Vec<TypedExpr>),
|
||||||
|
Variable(String),
|
||||||
|
Call(Box<TypedExpr>, Vec<TypedExpr>),
|
||||||
|
Index(Box<TypedExpr>, Box<TypedExpr>),
|
||||||
|
Dot(Box<TypedExpr>, String),
|
||||||
|
EarlyReturn(Option<Box<TypedExpr>>),
|
||||||
|
OptionalChain(Option<Box<TypedExpr>>, String),
|
||||||
|
Lambda(Vec<(String, Option<TypeAnnot>)>, Box<TypedExpr>),
|
||||||
|
Let(String, BindingKind, Option<TypeAnnot>, Box<TypedExpr>),
|
||||||
|
Assign(Box<TypedExpr>, Box<TypedExpr>),
|
||||||
|
Cast(Box<TypedExpr>, TypeAnnot),
|
||||||
|
If(Box<TypedExpr>, Box<TypedExpr>, Option<Box<TypedExpr>>),
|
||||||
|
Match(Box<TypedExpr>, Vec<(TypedPattern, TypedExpr)>),
|
||||||
|
While(Box<TypedExpr>, Box<TypedExpr>),
|
||||||
|
Do(Vec<TypedExpr>),
|
||||||
|
BinOp(Box<TypedExpr>, BinOp, Box<TypedExpr>),
|
||||||
|
UnOp(UnOp, Box<TypedExpr>),
|
||||||
|
For(String, Box<TypedExpr>, Box<TypedExpr>),
|
||||||
|
Range(Box<TypedExpr>, Box<TypedExpr>),
|
||||||
|
Return(Option<Box<TypedExpr>>),
|
||||||
|
Break,
|
||||||
|
Continue,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub struct TypedPattern {
|
||||||
|
pub kind: TypedPatternKind,
|
||||||
|
pub span: Span,
|
||||||
|
pub ty: Type,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone)]
|
||||||
|
pub enum TypedPatternKind {
|
||||||
|
Wildcard,
|
||||||
|
Variable(String),
|
||||||
|
Literal(String),
|
||||||
|
Tuple(Vec<TypedPattern>),
|
||||||
|
Struct(String, Vec<(String, TypedPattern)>),
|
||||||
|
Enum(String, String, Vec<TypedPattern>),
|
||||||
|
}
|
||||||
288
src/lexer/mod.rs
Normal file
288
src/lexer/mod.rs
Normal file
|
|
@ -0,0 +1,288 @@
|
||||||
|
use logos::Logos;
|
||||||
|
|
||||||
|
#[cfg(test)]
|
||||||
|
pub mod tests;
|
||||||
|
|
||||||
|
#[derive(Logos, Debug, PartialEq)]
|
||||||
|
#[logos(skip r"[ \n\r\t\f]+")] // Ignore this regex pattern between tokens
|
||||||
|
#[logos(skip r"#(.*)\n")] // Ignore this regex pattern between tokens
|
||||||
|
#[derive(Clone)]
|
||||||
|
pub enum Token {
|
||||||
|
#[regex(r"true|false", |lex| {
|
||||||
|
lex.slice().parse::<bool>().unwrap()
|
||||||
|
})]
|
||||||
|
Bool(bool),
|
||||||
|
|
||||||
|
#[regex(r"0|[1-9][0-9_]*", |lex| {
|
||||||
|
let s = lex.slice().replace("_", "");
|
||||||
|
// We parse to i64 for wider support.
|
||||||
|
s.parse::<i64>().unwrap()
|
||||||
|
}, priority = 4)]
|
||||||
|
Int(i64),
|
||||||
|
|
||||||
|
#[regex(r"(([0-9][0-9_]*\.[0-9_]+|[0-9]*\.[0-9_]+)([eE][+-]?[0-9_]+)?)", |lex| {
|
||||||
|
let s = lex.slice().replace("_", "");
|
||||||
|
s.parse::<f64>().unwrap()
|
||||||
|
}, priority = 3)]
|
||||||
|
Float(f64),
|
||||||
|
|
||||||
|
#[regex(r#""([^"\\]*(\\.[^"\\]*)*)""#, |lex| {
|
||||||
|
let s = lex.slice();
|
||||||
|
s[1..s.len()-1]
|
||||||
|
.replace("\\\"", "\"")
|
||||||
|
.replace("\\\\", "\\")
|
||||||
|
.replace("\\n", "\n")
|
||||||
|
.replace("\\r", "\r")
|
||||||
|
.replace("\\t", "\t")
|
||||||
|
})]
|
||||||
|
String(String),
|
||||||
|
|
||||||
|
#[regex(r#"r#"([^"]*)""#, |lex| {
|
||||||
|
let s = lex.slice();
|
||||||
|
// Remove the outer r" and " (s[2..s.len() - 1])
|
||||||
|
s[3..s.len() - 1].to_string()
|
||||||
|
})]
|
||||||
|
RawString(String),
|
||||||
|
|
||||||
|
#[regex(r"[a-zA-Z_][a-zA-Z0-9_]*", |lex|{
|
||||||
|
lex.slice().to_string()
|
||||||
|
})]
|
||||||
|
Variable(String),
|
||||||
|
|
||||||
|
#[token("bool")]
|
||||||
|
KeywordBool,
|
||||||
|
|
||||||
|
#[token("int")]
|
||||||
|
KeywordInt,
|
||||||
|
|
||||||
|
#[token("float")]
|
||||||
|
KeywordFloat,
|
||||||
|
|
||||||
|
#[token("string")]
|
||||||
|
KeywordString,
|
||||||
|
|
||||||
|
#[token("let")]
|
||||||
|
KeywordLet,
|
||||||
|
|
||||||
|
#[token("mut")]
|
||||||
|
KeywordMut,
|
||||||
|
|
||||||
|
#[token("uniq")]
|
||||||
|
KeywordUniq,
|
||||||
|
|
||||||
|
#[token("once")]
|
||||||
|
KeywordOnce,
|
||||||
|
|
||||||
|
#[token("if")]
|
||||||
|
KeywordIf,
|
||||||
|
|
||||||
|
#[token("then")]
|
||||||
|
KeywordThen,
|
||||||
|
|
||||||
|
#[token("else")]
|
||||||
|
KeywordElse,
|
||||||
|
|
||||||
|
#[token("fn")]
|
||||||
|
KeywordFn,
|
||||||
|
|
||||||
|
#[token("lambda")]
|
||||||
|
KeywordLambda,
|
||||||
|
|
||||||
|
#[token("do")]
|
||||||
|
KeywordDo,
|
||||||
|
|
||||||
|
#[token("end")]
|
||||||
|
KeywordEnd,
|
||||||
|
|
||||||
|
#[token("as")]
|
||||||
|
KeywordAs,
|
||||||
|
|
||||||
|
#[token("in")]
|
||||||
|
KeywordIn,
|
||||||
|
|
||||||
|
#[token("for")]
|
||||||
|
KeywordFor,
|
||||||
|
#[token("while")]
|
||||||
|
KeywordWhile,
|
||||||
|
|
||||||
|
#[token("loop")]
|
||||||
|
KeywordLoop,
|
||||||
|
|
||||||
|
#[token("where")]
|
||||||
|
KeywordWhere,
|
||||||
|
|
||||||
|
#[token("extern")]
|
||||||
|
KeywordExtern,
|
||||||
|
|
||||||
|
#[token("load")]
|
||||||
|
KeywordLoad,
|
||||||
|
|
||||||
|
#[token("from")]
|
||||||
|
KeywordFrom,
|
||||||
|
|
||||||
|
#[token("use")]
|
||||||
|
KeywordUse,
|
||||||
|
|
||||||
|
#[token("struct")]
|
||||||
|
KeywordStruct,
|
||||||
|
|
||||||
|
#[token("enum")]
|
||||||
|
KeywordEnum,
|
||||||
|
|
||||||
|
#[token("impl")]
|
||||||
|
KeywordImpl,
|
||||||
|
|
||||||
|
#[token("trait")]
|
||||||
|
KeywordTrait,
|
||||||
|
|
||||||
|
// #[token("type")]
|
||||||
|
// KeywordType,
|
||||||
|
//
|
||||||
|
#[token("match")]
|
||||||
|
KeywordMatch,
|
||||||
|
|
||||||
|
#[token("return")]
|
||||||
|
KeywordReturn,
|
||||||
|
|
||||||
|
#[token("break")]
|
||||||
|
KeywordBreak,
|
||||||
|
|
||||||
|
#[token("continue")]
|
||||||
|
KeywordContinue,
|
||||||
|
|
||||||
|
#[token("+")]
|
||||||
|
Plus,
|
||||||
|
|
||||||
|
#[token("-")]
|
||||||
|
Minus,
|
||||||
|
|
||||||
|
#[token("*")]
|
||||||
|
Mul,
|
||||||
|
|
||||||
|
#[token("/")]
|
||||||
|
Div,
|
||||||
|
|
||||||
|
#[token("%")]
|
||||||
|
Mod,
|
||||||
|
|
||||||
|
#[token("**", priority = 3)]
|
||||||
|
Power,
|
||||||
|
|
||||||
|
#[token("$")]
|
||||||
|
Dollar,
|
||||||
|
|
||||||
|
#[token("@")]
|
||||||
|
At,
|
||||||
|
|
||||||
|
#[token("==")]
|
||||||
|
Eq,
|
||||||
|
|
||||||
|
#[token("!=")]
|
||||||
|
NotEq,
|
||||||
|
|
||||||
|
#[token("<")]
|
||||||
|
Less,
|
||||||
|
|
||||||
|
#[token(">")]
|
||||||
|
Greater,
|
||||||
|
|
||||||
|
#[token("<=")]
|
||||||
|
LessEq,
|
||||||
|
|
||||||
|
#[token(">=")]
|
||||||
|
GreaterEq,
|
||||||
|
|
||||||
|
#[token("and")]
|
||||||
|
And,
|
||||||
|
|
||||||
|
#[token("or")]
|
||||||
|
Or,
|
||||||
|
|
||||||
|
#[token("xor")]
|
||||||
|
Xor,
|
||||||
|
|
||||||
|
#[token("nor")]
|
||||||
|
Nor,
|
||||||
|
|
||||||
|
#[token("not")]
|
||||||
|
Not,
|
||||||
|
|
||||||
|
#[token("(")]
|
||||||
|
LParen,
|
||||||
|
|
||||||
|
#[token(")")]
|
||||||
|
RParen,
|
||||||
|
|
||||||
|
#[token("[")]
|
||||||
|
LBracket,
|
||||||
|
|
||||||
|
#[token("]")]
|
||||||
|
RBracket,
|
||||||
|
|
||||||
|
#[token("{")]
|
||||||
|
LBrace,
|
||||||
|
|
||||||
|
#[token("}")]
|
||||||
|
RBrace,
|
||||||
|
|
||||||
|
#[token(",")]
|
||||||
|
Comma,
|
||||||
|
|
||||||
|
#[token(";")]
|
||||||
|
Semicolon,
|
||||||
|
|
||||||
|
#[token(":")]
|
||||||
|
Colon,
|
||||||
|
|
||||||
|
#[token(".")]
|
||||||
|
Dot,
|
||||||
|
|
||||||
|
#[token("...")]
|
||||||
|
Spread,
|
||||||
|
|
||||||
|
#[token("..")]
|
||||||
|
DotDot,
|
||||||
|
|
||||||
|
#[token("::")]
|
||||||
|
Access,
|
||||||
|
|
||||||
|
#[token("->")]
|
||||||
|
Arrow,
|
||||||
|
|
||||||
|
#[token("~")]
|
||||||
|
Tilde,
|
||||||
|
|
||||||
|
#[token("!")]
|
||||||
|
Bang,
|
||||||
|
|
||||||
|
// New tokens for pattern matching
|
||||||
|
#[token("=>")]
|
||||||
|
FatArrow, // For match arms
|
||||||
|
|
||||||
|
#[token("|")]
|
||||||
|
Union,
|
||||||
|
|
||||||
|
#[token("?.")]
|
||||||
|
OptionalChain,
|
||||||
|
|
||||||
|
#[token("?")]
|
||||||
|
Unwrap,
|
||||||
|
|
||||||
|
#[token("=")]
|
||||||
|
Assign,
|
||||||
|
|
||||||
|
#[token("+=")]
|
||||||
|
AddAssign,
|
||||||
|
|
||||||
|
#[token("-=")]
|
||||||
|
SubAssign,
|
||||||
|
|
||||||
|
#[token("*=")]
|
||||||
|
MulAssign,
|
||||||
|
|
||||||
|
#[token("/=")]
|
||||||
|
DivAssign,
|
||||||
|
|
||||||
|
#[token("%=")]
|
||||||
|
ModAssign,
|
||||||
|
}
|
||||||
229
src/lexer/tests.rs
Normal file
229
src/lexer/tests.rs
Normal file
|
|
@ -0,0 +1,229 @@
|
||||||
|
use super::Token;
|
||||||
|
use logos::Logos;
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_literals() {
|
||||||
|
let mut lexer = Token::lexer("true false 42 2.14 \"hello\" r\"raw\"");
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Bool(true))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Bool(false))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Int(42))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Float(2.14))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::String("hello".to_string()))));
|
||||||
|
// RawString regex seems to have issues, let's test separately
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Variable("r".to_string()))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::String("raw".to_string()))));
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_int_literals() {
|
||||||
|
let mut lexer = Token::lexer("0 123 1_000_000");
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Int(0))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Int(123))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Int(1000000))));
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_string_literals() {
|
||||||
|
let mut lexer = Token::lexer("\"hello world\" \"with\\\\escape\" \"quote\\\"here\"");
|
||||||
|
|
||||||
|
assert_eq!(
|
||||||
|
lexer.next(),
|
||||||
|
Some(Ok(Token::String("hello world".to_string())))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lexer.next(),
|
||||||
|
Some(Ok(Token::String("with\\escape".to_string())))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lexer.next(),
|
||||||
|
Some(Ok(Token::String("quote\"here".to_string())))
|
||||||
|
);
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_keywords() {
|
||||||
|
let mut lexer = Token::lexer(
|
||||||
|
"bool int float string let if else fn do end as in for while loop where extern import struct enum impl trait match return break continue",
|
||||||
|
);
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordBool)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordInt)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordFloat)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordString)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordLet)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordIf)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordElse)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordFn)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordDo)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordEnd)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordAs)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordIn)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordFor)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordWhile)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordLoop)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordWhere)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordExtern)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Variable("import".into()))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordStruct)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordEnum)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordImpl)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordTrait)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordMatch)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordReturn)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordBreak)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordContinue)));
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_operators() {
|
||||||
|
let mut lexer = Token::lexer("+ - * / % ** $ @ == != < > <= >= and or xor nor not");
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Plus)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Minus)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Mul)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Div)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Mod)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Power)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Dollar)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::At)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Eq)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::NotEq)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Less)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Greater)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::LessEq)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::GreaterEq)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::And)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Or)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Xor)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Nor)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Not)));
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_assignment_operators() {
|
||||||
|
let mut lexer = Token::lexer("= += -= *= /= %=");
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Assign)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::AddAssign)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::SubAssign)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::MulAssign)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::DivAssign)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::ModAssign)));
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_punctuation() {
|
||||||
|
let mut lexer = Token::lexer("( ) [ ] { } , ; : . ... .. :: -> ~ ! => | |> ?. ?");
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::LParen)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::RParen)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::LBracket)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::RBracket)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::LBrace)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::RBrace)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Comma)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Semicolon)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Colon)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Dot)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Spread)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::DotDot)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Access)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Arrow)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Tilde)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Bang)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::FatArrow)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Union)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::OptionalChain)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Unwrap)));
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_variables() {
|
||||||
|
let mut lexer = Token::lexer("x y_z _private camelCase PascalCase");
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Variable("x".to_string()))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Variable("y_z".to_string()))));
|
||||||
|
assert_eq!(
|
||||||
|
lexer.next(),
|
||||||
|
Some(Ok(Token::Variable("_private".to_string())))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lexer.next(),
|
||||||
|
Some(Ok(Token::Variable("camelCase".to_string())))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
lexer.next(),
|
||||||
|
Some(Ok(Token::Variable("PascalCase".to_string())))
|
||||||
|
);
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_whitespace_skipping() {
|
||||||
|
let mut lexer = Token::lexer(" \t\n\r true \n false ");
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Bool(true))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Bool(false))));
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_comment_skipping() {
|
||||||
|
let mut lexer = Token::lexer("true # this is a comment\n false");
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Bool(true))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Bool(false))));
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_complex_sequence() {
|
||||||
|
let mut lexer = Token::lexer("fn add(x: int, y: int) -> int { x + y }");
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordFn)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Variable("add".to_string()))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::LParen)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Variable("x".to_string()))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Colon)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordInt)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Comma)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Variable("y".to_string()))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Colon)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordInt)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::RParen)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Arrow)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordInt)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::LBrace)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Variable("x".to_string()))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Plus)));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::Variable("y".to_string()))));
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::RBrace)));
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn test_edge_cases() {
|
||||||
|
// Test that keywords are not treated as variables
|
||||||
|
let mut lexer = Token::lexer("let let_var if if_var");
|
||||||
|
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordLet)));
|
||||||
|
assert_eq!(
|
||||||
|
lexer.next(),
|
||||||
|
Some(Ok(Token::Variable("let_var".to_string())))
|
||||||
|
);
|
||||||
|
assert_eq!(lexer.next(), Some(Ok(Token::KeywordIf)));
|
||||||
|
assert_eq!(
|
||||||
|
lexer.next(),
|
||||||
|
Some(Ok(Token::Variable("if_var".to_string())))
|
||||||
|
);
|
||||||
|
assert_eq!(lexer.next(), None);
|
||||||
|
}
|
||||||
6
src/lib.rs
Normal file
6
src/lib.rs
Normal file
|
|
@ -0,0 +1,6 @@
|
||||||
|
pub const EXTENSION: &str = ".sui";
|
||||||
|
|
||||||
|
pub mod ast;
|
||||||
|
pub mod lexer;
|
||||||
|
pub mod parser;
|
||||||
|
pub mod typechecker;
|
||||||
88
src/main.rs
Normal file
88
src/main.rs
Normal file
|
|
@ -0,0 +1,88 @@
|
||||||
|
use logos::Logos;
|
||||||
|
use std::fs;
|
||||||
|
use suicmez::{lexer::Token, parser::Parser, typechecker::TypeChecker};
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
// Check if a file was provided as argument
|
||||||
|
let args: Vec<String> = std::env::args().collect();
|
||||||
|
if args.len() < 2 {
|
||||||
|
// Run all test files in the tests directory
|
||||||
|
run_test_suite();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
let filename = &args[1];
|
||||||
|
println!("Type checking file: {}", filename);
|
||||||
|
|
||||||
|
if let Err(e) = run_file(filename) {
|
||||||
|
eprintln!("Error: {}", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_test_suite() {
|
||||||
|
println!("Running test suite...\n");
|
||||||
|
|
||||||
|
let test_files = vec![
|
||||||
|
"tests/basic_types.sui",
|
||||||
|
"tests/structs.sui",
|
||||||
|
"tests/enums.sui",
|
||||||
|
"tests/functions.sui",
|
||||||
|
"tests/arrays.sui",
|
||||||
|
"tests/traits.sui",
|
||||||
|
"tests/control_flow.sui",
|
||||||
|
];
|
||||||
|
|
||||||
|
for file in test_files {
|
||||||
|
println!("Testing: {}", file);
|
||||||
|
match run_file(file) {
|
||||||
|
Ok(_) => println!("✓ Passed\n"),
|
||||||
|
Err(e) => println!("✗ Failed: {}\n", e),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn run_file(filename: &str) -> Result<(), String> {
|
||||||
|
// Read the source file
|
||||||
|
let source = fs::read_to_string(filename)
|
||||||
|
.map_err(|e| format!("Error reading file {}: {}", filename, e))?;
|
||||||
|
|
||||||
|
// First, we need to parse the source code
|
||||||
|
let mut tokens = Vec::new();
|
||||||
|
let mut lexer = Token::lexer(&source);
|
||||||
|
|
||||||
|
loop {
|
||||||
|
match lexer.next() {
|
||||||
|
Some(Ok(token)) => {
|
||||||
|
let span = lexer.span();
|
||||||
|
tokens.push((token, span));
|
||||||
|
}
|
||||||
|
Some(Err(_)) => {
|
||||||
|
return Err("Lexing error".to_string());
|
||||||
|
}
|
||||||
|
None => break,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let mut parser = Parser::new(filename.to_string(), tokens);
|
||||||
|
let ast_nodes = parser
|
||||||
|
.parse()
|
||||||
|
.map_err(|e| format!("Parse error: {}", e.message))?;
|
||||||
|
|
||||||
|
println!("Parsed {} AST nodes successfully", ast_nodes.len());
|
||||||
|
|
||||||
|
// Typecheck the AST
|
||||||
|
let mut typechecker = TypeChecker::new();
|
||||||
|
let typed_nodes = typechecker.typecheck_program(&ast_nodes).map_err(|e| {
|
||||||
|
format!(
|
||||||
|
"Type error at {}:{}: {:?}",
|
||||||
|
e.span.file, e.span.start, e.kind
|
||||||
|
)
|
||||||
|
})?;
|
||||||
|
|
||||||
|
println!(
|
||||||
|
"Type checking passed! {} nodes typechecked.",
|
||||||
|
typed_nodes.len()
|
||||||
|
);
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
1763
src/parser.rs
Normal file
1763
src/parser.rs
Normal file
File diff suppressed because it is too large
Load diff
4
src/typechecker.rs
Normal file
4
src/typechecker.rs
Normal file
|
|
@ -0,0 +1,4 @@
|
||||||
|
#[derive(Debug, Clone, PartialEq)]
|
||||||
|
pub enum Type {
|
||||||
|
Stub,
|
||||||
|
}
|
||||||
452
syntax_test.sui
Normal file
452
syntax_test.sui
Normal file
|
|
@ -0,0 +1,452 @@
|
||||||
|
use "std/abc"
|
||||||
|
load "math.so" as math_lib
|
||||||
|
|
||||||
|
extern print(msg: string) -> int from libc # this is a comment
|
||||||
|
extern malloc(size: int) -> int from libc #* this is a comment too *#
|
||||||
|
extern add_numbers(a: int, b: int) -> int from math_lib
|
||||||
|
|
||||||
|
# this is a funny comment
|
||||||
|
struct Point
|
||||||
|
x: int,
|
||||||
|
y: int
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Generic<T>
|
||||||
|
value: T,
|
||||||
|
tag: string
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Complex<T, U>
|
||||||
|
first: T,
|
||||||
|
second: U
|
||||||
|
end
|
||||||
|
|
||||||
|
struct EmptyStruct
|
||||||
|
end
|
||||||
|
|
||||||
|
struct ArrayStruct
|
||||||
|
numbers: [int],
|
||||||
|
matrix: [[float]]
|
||||||
|
end
|
||||||
|
|
||||||
|
enum Option<T>
|
||||||
|
Some(T),
|
||||||
|
None
|
||||||
|
end
|
||||||
|
|
||||||
|
enum Result<T, E>
|
||||||
|
Ok(T),
|
||||||
|
Err(E)
|
||||||
|
end
|
||||||
|
|
||||||
|
enum Status
|
||||||
|
Active,
|
||||||
|
Inactive,
|
||||||
|
Pending(string)
|
||||||
|
end
|
||||||
|
|
||||||
|
enum Tree<T>
|
||||||
|
Leaf(T),
|
||||||
|
Branch(T, T)
|
||||||
|
end
|
||||||
|
|
||||||
|
trait Show
|
||||||
|
fn display(msg: string) -> string,
|
||||||
|
fn to_string() -> string
|
||||||
|
end
|
||||||
|
|
||||||
|
trait Comparable<T>
|
||||||
|
fn compare(other: T) -> int
|
||||||
|
end
|
||||||
|
|
||||||
|
trait Container<T>
|
||||||
|
fn push(item: T) -> int,
|
||||||
|
fn pop() -> T
|
||||||
|
end
|
||||||
|
|
||||||
|
impl Point
|
||||||
|
fn new(x: int, y: int) -> Point
|
||||||
|
Point { x: x, y: y }
|
||||||
|
|
||||||
|
fn distance() -> float do
|
||||||
|
let x_sq = 5 * 5
|
||||||
|
let y_sq = 3 * 3
|
||||||
|
((x_sq + y_sq) as float)
|
||||||
|
end
|
||||||
|
|
||||||
|
fn move_by(dx: int, dy: int) -> Point do
|
||||||
|
let new_x = 5 + 10
|
||||||
|
let new_y = 3 + 20
|
||||||
|
Point { x: new_x, y: new_y }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
impl Option<int> : Show
|
||||||
|
fn display(msg: string) -> string
|
||||||
|
"Option value"
|
||||||
|
|
||||||
|
fn to_string() -> string
|
||||||
|
"option"
|
||||||
|
end
|
||||||
|
|
||||||
|
impl Result<string, int>
|
||||||
|
fn is_ok() -> bool
|
||||||
|
true
|
||||||
|
|
||||||
|
fn unwrap() -> string
|
||||||
|
"unwrapped"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn greet(name: string) -> string
|
||||||
|
"Hello, " + name
|
||||||
|
|
||||||
|
fn get_answer() -> int
|
||||||
|
42
|
||||||
|
|
||||||
|
fn print_number(num: int)
|
||||||
|
num
|
||||||
|
|
||||||
|
fn add(a: int, b: int) -> int
|
||||||
|
a + b
|
||||||
|
|
||||||
|
fn identity<T>(value: T) -> T
|
||||||
|
value
|
||||||
|
|
||||||
|
fn pair<T, U>(first: T, second: U) -> (T, U)
|
||||||
|
(first, second)
|
||||||
|
|
||||||
|
fn array_func(arr: [int]) -> [int]
|
||||||
|
arr
|
||||||
|
|
||||||
|
fn process<T>(item: T) -> string
|
||||||
|
"processed"
|
||||||
|
|
||||||
|
fn process_list(items: int) -> int do
|
||||||
|
let result = 0
|
||||||
|
result + items
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn literals() -> bool do
|
||||||
|
let int_val = 42
|
||||||
|
let float_val = 3.14
|
||||||
|
let negative = -100
|
||||||
|
let scientific = 1.5e-10
|
||||||
|
let bool_true = true
|
||||||
|
let bool_false = false
|
||||||
|
let string_val = "hello world"
|
||||||
|
let empty_string = ""
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn collections() -> bool do
|
||||||
|
let arr = [1, 2, 3, 4, 5]
|
||||||
|
let empty_arr = []
|
||||||
|
let tuple = (1, "hello", 3.14)
|
||||||
|
let single_tuple = (42)
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn struct_enum_literals() -> bool do
|
||||||
|
let point = Point { x: 10, y: 20 }
|
||||||
|
let option_val = Option::Some(42)
|
||||||
|
let option_none = Option::None
|
||||||
|
let result_ok = Result::Ok("success")
|
||||||
|
let result_err = Result::Err(404)
|
||||||
|
let status = Status::Pending("loading...")
|
||||||
|
true
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn arithmetic_logic() -> int do
|
||||||
|
let a = 10
|
||||||
|
let b = 5
|
||||||
|
let add_result = a + b
|
||||||
|
let sub_result = a - b
|
||||||
|
let mul_result = a * b
|
||||||
|
let div_result = a / b
|
||||||
|
let mod_result = a % b
|
||||||
|
|
||||||
|
let and_result = true and false
|
||||||
|
let or_result = true or false
|
||||||
|
let not_result = not true
|
||||||
|
|
||||||
|
let eq = a == b
|
||||||
|
let neq = a != b
|
||||||
|
let lt = a < b
|
||||||
|
let gt = a > b
|
||||||
|
let leq = a <= b
|
||||||
|
let geq = a >= b
|
||||||
|
|
||||||
|
add_result + sub_result
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn unary() -> int do
|
||||||
|
let a = 5
|
||||||
|
let neg = -a
|
||||||
|
let b = true
|
||||||
|
let not_b = not b
|
||||||
|
a + 1
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn bindings() -> int do
|
||||||
|
let x = 10
|
||||||
|
let mut y = 20
|
||||||
|
let uniq z = 30
|
||||||
|
let once w = 40
|
||||||
|
|
||||||
|
let typed: int = 100
|
||||||
|
let mut_typed: string = "hello"
|
||||||
|
|
||||||
|
x + 5
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn if_else(n: int) -> int
|
||||||
|
if n > 0
|
||||||
|
100
|
||||||
|
else
|
||||||
|
-100
|
||||||
|
|
||||||
|
fn if_else_complex(a: int, b: int) -> string
|
||||||
|
if a > b
|
||||||
|
"a is greater"
|
||||||
|
else
|
||||||
|
if a == b
|
||||||
|
"equal"
|
||||||
|
else
|
||||||
|
"b is greater"
|
||||||
|
|
||||||
|
|
||||||
|
fn match_simple(opt: Option<int>) -> int
|
||||||
|
match opt
|
||||||
|
Option::Some(x) => x,
|
||||||
|
Option::None => 0
|
||||||
|
end
|
||||||
|
|
||||||
|
fn match_complex(val: int) -> string
|
||||||
|
match val
|
||||||
|
0 => "zero",
|
||||||
|
1 => "one",
|
||||||
|
2 => "two",
|
||||||
|
_ => "many"
|
||||||
|
end
|
||||||
|
|
||||||
|
fn match_pattern(p: Point) -> string
|
||||||
|
match p
|
||||||
|
Point { x: 0, y: 0 } => "origin",
|
||||||
|
Point { x: x, y: y } => "point"
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn while_loop(n: int) -> int do
|
||||||
|
let mut count = 0
|
||||||
|
while count < n
|
||||||
|
do
|
||||||
|
count = count + 1
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn do_block() -> int
|
||||||
|
do
|
||||||
|
let a = 10
|
||||||
|
let b = 20
|
||||||
|
let c = 30
|
||||||
|
a + b + c
|
||||||
|
end
|
||||||
|
|
||||||
|
fn nested_do() -> int
|
||||||
|
do
|
||||||
|
let x = do
|
||||||
|
5
|
||||||
|
end
|
||||||
|
let y = do
|
||||||
|
10
|
||||||
|
end
|
||||||
|
x + y
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn function_calls() -> int do
|
||||||
|
let point = Point { x: 5, y: 10 }
|
||||||
|
let x_coord = point.x
|
||||||
|
let arr = [1, 2, 3]
|
||||||
|
let first = arr[0]
|
||||||
|
|
||||||
|
let result = add(10, 20)
|
||||||
|
let identity_val = identity(42)
|
||||||
|
|
||||||
|
result + first
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn optional_chain(opt: Option<Point>) -> int do
|
||||||
|
let val = opt?.x
|
||||||
|
100
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn early_return() -> int do
|
||||||
|
let opt = Option::Some(42)
|
||||||
|
let val = opt?
|
||||||
|
val
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
fn casting() -> float do
|
||||||
|
let int_val = 42
|
||||||
|
let float_val = (int_val as float)
|
||||||
|
let x = (100 as float) + 3.14
|
||||||
|
x
|
||||||
|
end
|
||||||
|
|
||||||
|
fn lambda_example() -> int do
|
||||||
|
let add_one = lambda (x) x + 1
|
||||||
|
let multiply = lambda (x, y) x * y
|
||||||
|
let get_five = lambda () 5
|
||||||
|
|
||||||
|
let applied = add_one(10)
|
||||||
|
applied
|
||||||
|
end
|
||||||
|
|
||||||
|
fn assignment() -> int do
|
||||||
|
let mut x = 10
|
||||||
|
x = 20
|
||||||
|
x = x + 5
|
||||||
|
x
|
||||||
|
end
|
||||||
|
|
||||||
|
fn with_return(n: int) -> int do
|
||||||
|
if n < 0
|
||||||
|
return -1
|
||||||
|
n + 100
|
||||||
|
end
|
||||||
|
|
||||||
|
fn with_break() -> int do
|
||||||
|
let mut i = 0
|
||||||
|
while i < 10
|
||||||
|
do
|
||||||
|
if i == 5
|
||||||
|
break
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
i
|
||||||
|
end
|
||||||
|
|
||||||
|
fn with_continue() -> int do
|
||||||
|
let mut sum = 0
|
||||||
|
let mut i = 0
|
||||||
|
while i < 10
|
||||||
|
do
|
||||||
|
i = i + 1
|
||||||
|
if i % 2 == 0
|
||||||
|
continue
|
||||||
|
sum = sum + i
|
||||||
|
end
|
||||||
|
sum
|
||||||
|
end
|
||||||
|
|
||||||
|
|
||||||
|
@deprecated("use new_func instead")
|
||||||
|
fn old_func() -> int
|
||||||
|
42
|
||||||
|
|
||||||
|
@optimize(level = aggressive)
|
||||||
|
fn fast_func() -> int
|
||||||
|
100
|
||||||
|
|
||||||
|
@test
|
||||||
|
fn test_something() -> bool
|
||||||
|
true
|
||||||
|
|
||||||
|
fn fibonacci(n: int) -> int
|
||||||
|
if n <= 1
|
||||||
|
n
|
||||||
|
else
|
||||||
|
fibonacci(n - 1) + fibonacci(n - 2)
|
||||||
|
|
||||||
|
fn factorial(n: int) -> int do
|
||||||
|
let mut result = 1
|
||||||
|
let mut i = 2
|
||||||
|
while i <= n
|
||||||
|
do
|
||||||
|
result = result * i
|
||||||
|
i = i + 1
|
||||||
|
end
|
||||||
|
result
|
||||||
|
end
|
||||||
|
|
||||||
|
fn map_over_option<T, U>(opt: Option<T>) -> Option<U>
|
||||||
|
match opt
|
||||||
|
Option::Some(x) => Option::Some(x),
|
||||||
|
Option::None => Option::None
|
||||||
|
end
|
||||||
|
|
||||||
|
fn process_result<T, E>(res: Result<T, E>) -> int
|
||||||
|
match res
|
||||||
|
Result::Ok(x) => 1,
|
||||||
|
Result::Err(e) => 0
|
||||||
|
end
|
||||||
|
|
||||||
|
struct LinkedList<T>
|
||||||
|
value: T,
|
||||||
|
next: Option<int>
|
||||||
|
end
|
||||||
|
|
||||||
|
impl LinkedList<int>
|
||||||
|
fn new(v: int) -> LinkedList<int>
|
||||||
|
LinkedList { value: 42, next: Option::None }
|
||||||
|
|
||||||
|
fn head() -> int
|
||||||
|
100
|
||||||
|
|
||||||
|
fn tail() -> Option<int>
|
||||||
|
Option::None
|
||||||
|
|
||||||
|
fn sum() -> int
|
||||||
|
do
|
||||||
|
let mut total = 0
|
||||||
|
total
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
fn complex_pattern_match(val: int) -> string
|
||||||
|
match val
|
||||||
|
0 => "zero",
|
||||||
|
1 => "one",
|
||||||
|
2 => "two",
|
||||||
|
3 => "three",
|
||||||
|
4 => "four",
|
||||||
|
5 => "five",
|
||||||
|
_ => "many"
|
||||||
|
end
|
||||||
|
|
||||||
|
fn tuple_destructure() -> int do
|
||||||
|
let tup = (10, 20, 30)
|
||||||
|
30
|
||||||
|
end
|
||||||
|
|
||||||
|
fn array_ops() -> int do
|
||||||
|
let arr = [1, 2, 3, 4, 5]
|
||||||
|
let first = arr[0]
|
||||||
|
let length = 5
|
||||||
|
first + length
|
||||||
|
end
|
||||||
|
|
||||||
|
fn range_example() -> int do
|
||||||
|
let r = 1..10
|
||||||
|
5
|
||||||
|
end
|
||||||
|
|
||||||
|
fn for_loop_example() -> int do
|
||||||
|
let sum = 0
|
||||||
|
for i in 0..5
|
||||||
|
sum + i
|
||||||
|
sum
|
||||||
|
end
|
||||||
7
tests/arrays.sui
Normal file
7
tests/arrays.sui
Normal file
|
|
@ -0,0 +1,7 @@
|
||||||
|
# Array test
|
||||||
|
fn main(args: [string]) -> int do
|
||||||
|
let arr = [1, 2, 3, 4];
|
||||||
|
let empty = [];
|
||||||
|
let x = arr[0];
|
||||||
|
x
|
||||||
|
end
|
||||||
8
tests/basic_types.sui
Normal file
8
tests/basic_types.sui
Normal file
|
|
@ -0,0 +1,8 @@
|
||||||
|
# Basic type checking test
|
||||||
|
fn main() -> int do
|
||||||
|
let x = 5;
|
||||||
|
let y = 10.5;
|
||||||
|
let z = true;
|
||||||
|
let s = "hello";
|
||||||
|
x
|
||||||
|
end
|
||||||
12
tests/control_flow.sui
Normal file
12
tests/control_flow.sui
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Control flow test
|
||||||
|
fn main() -> int do
|
||||||
|
if true
|
||||||
|
1
|
||||||
|
else
|
||||||
|
0
|
||||||
|
|
||||||
|
let i = 0;
|
||||||
|
while i < 5
|
||||||
|
i = i + 1;
|
||||||
|
i
|
||||||
|
end
|
||||||
34
tests/enums.sui
Normal file
34
tests/enums.sui
Normal file
|
|
@ -0,0 +1,34 @@
|
||||||
|
# Enum test
|
||||||
|
enum Color
|
||||||
|
Red,
|
||||||
|
Green,
|
||||||
|
Blue,
|
||||||
|
end
|
||||||
|
|
||||||
|
enum Result<T, E>
|
||||||
|
Ok(T),
|
||||||
|
Err(E),
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Person
|
||||||
|
name: string,
|
||||||
|
age: int,
|
||||||
|
end
|
||||||
|
|
||||||
|
fn main() -> int do
|
||||||
|
let color = Color::Red();
|
||||||
|
let res = Result::Ok(42);
|
||||||
|
match res
|
||||||
|
Result::Ok(value) => value,
|
||||||
|
Result::Err(_) => 0,
|
||||||
|
end
|
||||||
|
let person = Person { name: "Bob", age: 25 };
|
||||||
|
match color
|
||||||
|
Color::Red() => 1,
|
||||||
|
Color::Green() => 2,
|
||||||
|
Color::Blue() => 3,
|
||||||
|
end
|
||||||
|
match person
|
||||||
|
Person { name: _, age: age } => age,
|
||||||
|
end
|
||||||
|
end
|
||||||
12
tests/functions.sui
Normal file
12
tests/functions.sui
Normal file
|
|
@ -0,0 +1,12 @@
|
||||||
|
# Function test
|
||||||
|
fn add(x: int, y: int) -> int
|
||||||
|
x + y
|
||||||
|
|
||||||
|
fn identity<T>(x: T) -> T
|
||||||
|
x
|
||||||
|
|
||||||
|
fn main() -> int do
|
||||||
|
let sum = add(5, 3);
|
||||||
|
let id = identity(42);
|
||||||
|
sum
|
||||||
|
end
|
||||||
16
tests/structs.sui
Normal file
16
tests/structs.sui
Normal file
|
|
@ -0,0 +1,16 @@
|
||||||
|
# Struct test
|
||||||
|
struct Point
|
||||||
|
x: int,
|
||||||
|
y: int,
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Person<T>
|
||||||
|
name: string,
|
||||||
|
age: T,
|
||||||
|
end
|
||||||
|
|
||||||
|
fn main() -> int do
|
||||||
|
let p = Point { x: 5, y: 10 };
|
||||||
|
let person = Person { name: "Alice", age: 30 };
|
||||||
|
p.x
|
||||||
|
end
|
||||||
17
tests/traits.sui
Normal file
17
tests/traits.sui
Normal file
|
|
@ -0,0 +1,17 @@
|
||||||
|
# Trait and implementation test
|
||||||
|
trait Show
|
||||||
|
fn show(self) -> string
|
||||||
|
end
|
||||||
|
|
||||||
|
struct Number
|
||||||
|
value: int
|
||||||
|
end
|
||||||
|
|
||||||
|
impl Number : Show
|
||||||
|
fn show(self) -> string
|
||||||
|
"number"
|
||||||
|
end
|
||||||
|
|
||||||
|
fn main() -> int do
|
||||||
|
42
|
||||||
|
end
|
||||||
Loading…
Add table
Add a link
Reference in a new issue