├── lib ├── std │ ├── io │ │ ├── path.ark │ │ ├── file.ark │ │ └── println.ark │ ├── mem │ │ ├── raw.ark │ │ └── mem.ark │ ├── adt │ │ ├── linkedlist.ark │ │ ├── hashmap.ark │ │ └── stack.ark │ ├── rand │ │ └── pcg.ark │ ├── strings │ │ ├── util.ark │ │ └── string.ark │ └── unicode │ │ └── utf8 │ │ └── coding.ark ├── tests │ ├── test-regex.ark │ ├── test.ark │ ├── test-hashmap.ark │ ├── test-linkedlist.ark │ ├── test-stack.ark │ ├── test-string.ark │ ├── test-dynamic-string.ark │ ├── test-fileio.ark │ └── test-utf8.ark ├── Makefile ├── CONTRIBUTING.md ├── README.md ├── LICENSE └── design.md ├── tests ├── main.ark ├── assign.ark ├── unused_func.ark ├── single_line_func.ark ├── variadic.ark ├── int_lit_overflow.ark ├── generic_inferrence.ark ├── pointer_index.ark ├── global.ark ├── destructuring_assign.ark ├── novoid.ark ├── unused_var.ark ├── arrays │ ├── array_bound.ark │ ├── array.toml │ ├── array_len.toml │ ├── array_bound.toml │ ├── array_global.toml │ ├── array_len.ark │ ├── array_global.ark │ └── array.ark ├── string.ark ├── if.toml ├── issues │ ├── 720.ark │ ├── 719.toml │ ├── 720.toml │ └── 719.ark ├── args.toml ├── bool.toml ├── cast.toml ├── constant_ref.ark ├── enum.toml ├── for.toml ├── func.toml ├── main.toml ├── assign.toml ├── c_ints.toml ├── defer.toml ├── deref.toml ├── global.toml ├── match.toml ├── method.toml ├── novoid.toml ├── option.toml ├── reference.ark ├── sizeof.toml ├── string.toml ├── struct.toml ├── tuple.toml ├── generic.toml ├── interface.toml ├── pointer.toml ├── prototype.ark ├── stdlib │ ├── list.toml │ ├── utf8.toml │ ├── random.ark │ ├── random.toml │ ├── list.ark │ └── utf8.ark ├── call_conv.toml ├── fibonacci.toml ├── float_ops.toml ├── iterator.toml ├── ownership.toml ├── prototype.toml ├── recursive.toml ├── reference.toml ├── variadic.toml ├── func_inline.toml ├── mutable_ref.toml ├── named_types.toml ├── prime_sieve.toml ├── binop_assign.toml ├── constant_ref.toml ├── float_suffix.toml ├── func_pointer.toml ├── nested_scope.toml ├── stdform_nums.toml ├── func_type_mismatch.ark ├── generic_member.toml ├── nested_comment.toml ├── pointer_index.toml ├── for_conditional.toml ├── single_line_func.toml ├── single_stat_func.toml ├── variable_shadow.toml ├── variable_zeroing.toml ├── int_lit_overflow.toml ├── cast_call_ambiguity.toml ├── func_inline.ark ├── func_type_mismatch.toml ├── generic_inferrence.toml ├── destructuring_assign.toml ├── nested_comment.ark ├── enum_union.toml ├── fibonacci.ark ├── args.ark ├── mutable_ref.ark ├── scanf_test.toml ├── variable_shadow.ark ├── call_conv.ark ├── scanf_test.ark ├── for_conditional.ark ├── cast_call_ambiguity.ark ├── recursive.ark ├── enum.ark ├── tuple.ark ├── bool.ark ├── ownership.ark ├── float_suffix.ark ├── nested_scope.ark ├── pointer.ark ├── stdform_nums.ark ├── variable_zeroing.ark ├── deref.ark ├── single_stat_func.ark ├── generic_member.ark ├── float_ops.ark ├── option.ark ├── binop_assign.ark ├── c_ints.ark ├── match.ark ├── fizzbuzz.ark ├── func_pointer.ark ├── enum_union.ark ├── interface.ark ├── sizeof.ark ├── for.ark ├── cast.ark ├── func.ark ├── defer.ark ├── iterator.ark ├── struct.ark ├── method.ark ├── fizzbuzz.toml ├── prime_sieve.ark ├── if.ark ├── generic.ark └── named_types.ark ├── examples ├── README.md ├── vm.ark ├── factorial.ark ├── higher_or_lower.ark ├── pi_monte_carlo.ark └── prime.ark ├── src ├── util │ ├── exitstatus.go │ ├── misc.go │ ├── log │ │ ├── timed.go │ │ └── log.go │ └── color.go ├── parser │ ├── unoptype_string.go │ ├── binoptype_string.go │ ├── misc.go │ ├── attr.go │ ├── keywords.go │ └── operators.go ├── ast │ ├── runtime.go │ ├── primitivetype_string.go │ ├── misc.go │ ├── dependency.go │ ├── module.go │ ├── mangle.go │ └── scope.go ├── doc │ ├── markdown.go │ ├── index.go │ ├── style.go │ ├── doc.go │ ├── decl.go │ └── file.go ├── codegen │ ├── codegen.go │ └── LLVMCodegen │ │ └── binary.go ├── semantic │ ├── misc.go │ ├── immutable_assign.go │ ├── deprecated.go │ ├── use_before_declare.go │ ├── break_next.go │ ├── unused.go │ ├── unreachable.go │ ├── recursive.go │ ├── reference.go │ ├── semantic.go │ └── attributes.go ├── lexer │ ├── token.go │ └── sourcefile.go └── cmd │ └── ark │ ├── args.go │ └── runtime.go ├── Makefile ├── .gitignore ├── tools └── make-test.sh ├── .travis.yml ├── LICENSE ├── README.md └── CONTRIBUTING.md /lib/std/io/path.ark: -------------------------------------------------------------------------------- 1 | // TODO path utility funcs? -------------------------------------------------------------------------------- /tests/main.ark: -------------------------------------------------------------------------------- 1 | pub func main() -> int { 2 | return 0; 3 | } 4 | -------------------------------------------------------------------------------- /examples/README.md: -------------------------------------------------------------------------------- 1 | # Examples 2 | Some examples of simple Ark programs, -------------------------------------------------------------------------------- /examples/vm.ark: -------------------------------------------------------------------------------- 1 | // check it out on GitHub! 2 | // https://github.com/ark-lang/mac-ark 3 | -------------------------------------------------------------------------------- /lib/tests/test-regex.ark: -------------------------------------------------------------------------------- 1 | #use std::regex 2 | 3 | pub func main() -> int { 4 | 5 | return 0; 6 | } -------------------------------------------------------------------------------- /tests/assign.ark: -------------------------------------------------------------------------------- 1 | pub func main() -> int { 2 | mut x: int = 5; 3 | x = 10; 4 | return x - 10; 5 | } 6 | -------------------------------------------------------------------------------- /lib/tests/test.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | 3 | pub func main() -> int { 4 | io::println("hello world"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/unused_func.ark: -------------------------------------------------------------------------------- 1 | func add() { 2 | 3 | } 4 | 5 | pub func main() -> int { 6 | add(); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/single_line_func.ark: -------------------------------------------------------------------------------- 1 | func add(a: int, b: int) -> int => return a + b; 2 | 3 | pub func main() -> int => return add(0, 0); 4 | -------------------------------------------------------------------------------- /lib/tests/test-hashmap.ark: -------------------------------------------------------------------------------- 1 | #use std::adt 2 | 3 | pub func main() -> int { 4 | map := adt::HashMap::new(); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /tests/variadic.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | C::printf(c"hi"); 5 | return 0; 6 | } 7 | -------------------------------------------------------------------------------- /tests/int_lit_overflow.ark: -------------------------------------------------------------------------------- 1 | pub func main() -> int { 2 | a: u8 = 256; 3 | b: u8 = -1; 4 | c: s8 = 128; 5 | d: s8 = -129; 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/generic_inferrence.ark: -------------------------------------------------------------------------------- 1 | func deref(v: ^T) -> T { 2 | return @v; 3 | } 4 | 5 | pub func main() -> int { 6 | i: int = 0; 7 | ip := ^i; 8 | return deref(ip); 9 | } 10 | -------------------------------------------------------------------------------- /tests/pointer_index.ark: -------------------------------------------------------------------------------- 1 | #[c] func malloc(size: uint) -> ^mut u8; 2 | 3 | pub func main() -> int { 4 | it := C::malloc(64); 5 | 6 | it[10] = 0; 7 | 8 | return int(it[10]); 9 | } -------------------------------------------------------------------------------- /tests/global.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | mut x: int = 10; 4 | 5 | pub func main() -> int { 6 | x = 20; 7 | C::printf(c"%d\n", x); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /tests/destructuring_assign.ark: -------------------------------------------------------------------------------- 1 | pub func main() -> int { 2 | mut a: u16 = 42; 3 | mut b: int = 31; 4 | 5 | (_, a, _, b) = (-4, 16, 242, 32); 6 | (b, _) -= (int(a), 0); 7 | 8 | return b - 16; 9 | } -------------------------------------------------------------------------------- /tests/novoid.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | func sayHi() { 4 | C::printf(c"hi\n"); 5 | } 6 | 7 | pub func main() -> int { 8 | sayHi(); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /tests/unused_var.ark: -------------------------------------------------------------------------------- 1 | func printf(y: int) { 2 | 3 | } 4 | 5 | pub func main() -> int { 6 | mut x: int = 0; 7 | 8 | y := x + x; 9 | 10 | printf(y); 11 | 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/arrays/array_bound.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...); 2 | 3 | pub func main() -> int { 4 | x := []int{0, 1, 2, 3, 4, 5}; 5 | y := x[7]; 6 | C::printf(c"%d\n", y); 7 | return 0; 8 | } 9 | -------------------------------------------------------------------------------- /tests/string.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | 3 | pub func main() -> int { 4 | mut str := "hello!"; 5 | str[1] = u8('a'); 6 | io::println(str); 7 | 8 | io::println("hi"); 9 | return 0; 10 | } 11 | -------------------------------------------------------------------------------- /src/util/exitstatus.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | const ( 4 | EXIT_SUCCESS int = iota 5 | EXIT_FAILURE_SETUP 6 | EXIT_FAILURE_PARSE 7 | EXIT_FAILURE_CONSTRUCTOR 8 | EXIT_FAILURE_SEMANTIC 9 | EXIT_FAILURE_CODEGEN 10 | ) 11 | -------------------------------------------------------------------------------- /lib/tests/test-linkedlist.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | #use std::adt 3 | 4 | pub func main() -> int { 5 | /* 6 | myLinkedList := adt::LinkedList::new(); 7 | defer myLinkedList.destroy(); 8 | */ 9 | return 0; 10 | } -------------------------------------------------------------------------------- /src/util/misc.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "unicode" 5 | ) 6 | 7 | func CapitalizeFirst(s string) string { 8 | runes := []rune(s) 9 | 10 | runes[0] = unicode.ToTitle(runes[0]) 11 | 12 | return string(runes) 13 | } 14 | -------------------------------------------------------------------------------- /tests/if.toml: -------------------------------------------------------------------------------- 1 | Name = "if" 2 | Sourcefile = "if.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/issues/720.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | #use std::unicode::utf8 3 | 4 | pub func main() -> int { 5 | num := utf8::numBytes([]rune{'R', 'u', 'n', 'e'}); 6 | io::printUint(num); 7 | io::println(""); 8 | return 4 - int(num); 9 | } 10 | -------------------------------------------------------------------------------- /tests/args.toml: -------------------------------------------------------------------------------- 1 | Name = "args" 2 | Sourcefile = "args.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/bool.toml: -------------------------------------------------------------------------------- 1 | Name = "bool" 2 | Sourcefile = "bool.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/cast.toml: -------------------------------------------------------------------------------- 1 | Name = "cast" 2 | Sourcefile = "cast.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/constant_ref.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | mut x: int = 1337; 5 | y: &int = &x; 6 | _ = y; 7 | x = 32; 8 | C::printf(c"%d\n", x); 9 | 10 | return 0; 11 | } 12 | -------------------------------------------------------------------------------- /tests/enum.toml: -------------------------------------------------------------------------------- 1 | Name = "enum" 2 | Sourcefile = "enum.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/for.toml: -------------------------------------------------------------------------------- 1 | Name = "for" 2 | Sourcefile = "for.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/func.toml: -------------------------------------------------------------------------------- 1 | Name = "func" 2 | Sourcefile = "func.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/main.toml: -------------------------------------------------------------------------------- 1 | Name = "main" 2 | Sourcefile = "main.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/assign.toml: -------------------------------------------------------------------------------- 1 | Name = "assign" 2 | Sourcefile = "assign.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/c_ints.toml: -------------------------------------------------------------------------------- 1 | Name = "c_ints" 2 | Sourcefile = "c_ints.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/defer.toml: -------------------------------------------------------------------------------- 1 | Name = "defer" 2 | Sourcefile = "defer.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/deref.toml: -------------------------------------------------------------------------------- 1 | Name = "deref" 2 | Sourcefile = "deref.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/global.toml: -------------------------------------------------------------------------------- 1 | Name = "global" 2 | Sourcefile = "global.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/match.toml: -------------------------------------------------------------------------------- 1 | Name = "match" 2 | Sourcefile = "match.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/method.toml: -------------------------------------------------------------------------------- 1 | Name = "method" 2 | Sourcefile = "method.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/novoid.toml: -------------------------------------------------------------------------------- 1 | Name = "novoid" 2 | Sourcefile = "novoid.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/option.toml: -------------------------------------------------------------------------------- 1 | Name = "option" 2 | Sourcefile = "option.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/reference.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | mut y: int = 21; 5 | x: &int = &y; 6 | y = 31; 7 | C::printf(c"yo y was 21 and should be 31? `%d`\n", ^x); 8 | return 0; 9 | } 10 | -------------------------------------------------------------------------------- /tests/sizeof.toml: -------------------------------------------------------------------------------- 1 | Name = "sizeof" 2 | Sourcefile = "sizeof.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/string.toml: -------------------------------------------------------------------------------- 1 | Name = "string" 2 | Sourcefile = "string.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/struct.toml: -------------------------------------------------------------------------------- 1 | Name = "struct" 2 | Sourcefile = "struct.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/tuple.toml: -------------------------------------------------------------------------------- 1 | Name = "tuple" 2 | Sourcefile = "tuple.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/arrays/array.toml: -------------------------------------------------------------------------------- 1 | Name = "array" 2 | Sourcefile = "array.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/generic.toml: -------------------------------------------------------------------------------- 1 | Name = "generic" 2 | Sourcefile = "generic.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/interface.toml: -------------------------------------------------------------------------------- 1 | Name = "interface" 2 | Sourcefile = "interface.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" -------------------------------------------------------------------------------- /tests/pointer.toml: -------------------------------------------------------------------------------- 1 | Name = "pointer" 2 | Sourcefile = "pointer.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/prototype.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | C::printf(c"finally, it works!\n"); 5 | C::printf(c"we have to specify the c attribute so it doesn't mangle the name %d\n", 5); 6 | return 0; 7 | } 8 | -------------------------------------------------------------------------------- /tests/stdlib/list.toml: -------------------------------------------------------------------------------- 1 | Name = "list" 2 | Sourcefile = "list.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/stdlib/utf8.toml: -------------------------------------------------------------------------------- 1 | Name = "utf8" 2 | Sourcefile = "utf8.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/call_conv.toml: -------------------------------------------------------------------------------- 1 | Name = "call_conv" 2 | Sourcefile = "call_conv.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/fibonacci.toml: -------------------------------------------------------------------------------- 1 | Name = "fibonacci" 2 | Sourcefile = "fibonacci.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/float_ops.toml: -------------------------------------------------------------------------------- 1 | Name = "float_ops" 2 | Sourcefile = "float_ops.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/issues/719.toml: -------------------------------------------------------------------------------- 1 | Name = "Issue #719" 2 | Sourcefile = "719.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = """""" 14 | -------------------------------------------------------------------------------- /tests/iterator.toml: -------------------------------------------------------------------------------- 1 | Name = "iterator" 2 | Sourcefile = "iterator.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/ownership.toml: -------------------------------------------------------------------------------- 1 | Name = "ownership" 2 | Sourcefile = "ownership.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/prototype.toml: -------------------------------------------------------------------------------- 1 | Name = "prototype" 2 | Sourcefile = "prototype.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/recursive.toml: -------------------------------------------------------------------------------- 1 | Name = "recursive" 2 | Sourcefile = "recursive.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/reference.toml: -------------------------------------------------------------------------------- 1 | Name = "reference" 2 | Sourcefile = "reference.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/variadic.toml: -------------------------------------------------------------------------------- 1 | Name = "variadic" 2 | Sourcefile = "variadic.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | .PHONY: all fmt gen wc 2 | 3 | all: 4 | @go install -gcflags '-N -l' github.com/ark-lang/ark/src/... 5 | 6 | fmt: 7 | go fmt github.com/ark-lang/ark/... 8 | 9 | gen: 10 | go generate ./... 11 | 12 | wc: 13 | find src/ -name *.go | xargs wc 14 | -------------------------------------------------------------------------------- /tests/func_inline.toml: -------------------------------------------------------------------------------- 1 | Name = "func_inline" 2 | Sourcefile = "func_inline.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/mutable_ref.toml: -------------------------------------------------------------------------------- 1 | Name = "mutable_ref" 2 | Sourcefile = "mutable_ref.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/named_types.toml: -------------------------------------------------------------------------------- 1 | Name = "named_types" 2 | Sourcefile = "named_types.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/prime_sieve.toml: -------------------------------------------------------------------------------- 1 | Name = "prime_sieve" 2 | Sourcefile = "prime_sieve.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/arrays/array_len.toml: -------------------------------------------------------------------------------- 1 | Name = "array_len" 2 | Sourcefile = "array_len.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/binop_assign.toml: -------------------------------------------------------------------------------- 1 | Name = "binop_assign" 2 | Sourcefile = "binop_assign.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/constant_ref.toml: -------------------------------------------------------------------------------- 1 | Name = "constant_ref" 2 | Sourcefile = "constant_ref.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/float_suffix.toml: -------------------------------------------------------------------------------- 1 | Name = "float_suffix" 2 | Sourcefile = "float_suffix.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/func_pointer.toml: -------------------------------------------------------------------------------- 1 | Name = "func_pointer" 2 | Sourcefile = "func_pointer.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/issues/720.toml: -------------------------------------------------------------------------------- 1 | Name = "Issue #720" 2 | Sourcefile = "720.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = """4 14 | """ 15 | -------------------------------------------------------------------------------- /tests/nested_scope.toml: -------------------------------------------------------------------------------- 1 | Name = "nested_scope" 2 | Sourcefile = "nested_scope.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/stdform_nums.toml: -------------------------------------------------------------------------------- 1 | Name = "stdform_nums" 2 | Sourcefile = "stdform_nums.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/arrays/array_bound.toml: -------------------------------------------------------------------------------- 1 | Name = "array_bound" 2 | Sourcefile = "array_bound.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = -1 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/arrays/array_global.toml: -------------------------------------------------------------------------------- 1 | Name = "array_global" 2 | Sourcefile = "array_global.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/func_type_mismatch.ark: -------------------------------------------------------------------------------- 1 | pub func main() -> int { 2 | return invoke(sub, 55, 55); 3 | } 4 | 5 | func sub(a: int, b: int) -> int { 6 | return a - b; 7 | } 8 | 9 | func invoke(fn: func(int, bool) -> int, a: int, b: int) -> int { 10 | return fn(a, b); 11 | } 12 | -------------------------------------------------------------------------------- /tests/generic_member.toml: -------------------------------------------------------------------------------- 1 | Name = "generic_member" 2 | Sourcefile = "generic_member.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/nested_comment.toml: -------------------------------------------------------------------------------- 1 | Name = "nested_comment" 2 | Sourcefile = "nested_comment.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/pointer_index.toml: -------------------------------------------------------------------------------- 1 | Name = "pointer_index" 2 | Sourcefile = "pointer_index.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/for_conditional.toml: -------------------------------------------------------------------------------- 1 | Name = "for_conditional" 2 | Sourcefile = "for_conditional.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/single_line_func.toml: -------------------------------------------------------------------------------- 1 | Name = "single_line_func" 2 | Sourcefile = "single_line_func.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/single_stat_func.toml: -------------------------------------------------------------------------------- 1 | Name = "single_stat_func" 2 | Sourcefile = "single_stat_func.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/variable_shadow.toml: -------------------------------------------------------------------------------- 1 | Name = "variable_shadow" 2 | Sourcefile = "variable_shadow.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/variable_zeroing.toml: -------------------------------------------------------------------------------- 1 | Name = "variable_zeroing" 2 | Sourcefile = "variable_zeroing.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/int_lit_overflow.toml: -------------------------------------------------------------------------------- 1 | Name = "int_lit_overflow" 2 | Sourcefile = "int_lit_overflow.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = -1 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/issues/719.ark: -------------------------------------------------------------------------------- 1 | type Thing struct{}; 2 | 3 | func (this: Thing) self() -> Thing { 4 | return this; 5 | } 6 | 7 | func (this: Thing) array() -> []int { 8 | return {0}; 9 | } 10 | 11 | pub func main() -> int { 12 | t: Thing; 13 | return t.self().self().array()[0]; 14 | } -------------------------------------------------------------------------------- /lib/std/mem/raw.ark: -------------------------------------------------------------------------------- 1 | #[c] func malloc(size: uint) -> ^C::void; 2 | #[c] func free(ptr: ^C::void); 3 | 4 | pub func rawAlloc(bytes: uint) -> uintptr { 5 | return uintptr(C::malloc(bytes)); 6 | } 7 | 8 | pub func rawFree(ptr: uintptr) { 9 | C::free((^C::void)(ptr)); 10 | } 11 | -------------------------------------------------------------------------------- /tests/cast_call_ambiguity.toml: -------------------------------------------------------------------------------- 1 | Name = "cast_call_ambiguity" 2 | Sourcefile = "cast_call_ambiguity.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/func_inline.ark: -------------------------------------------------------------------------------- 1 | pub func main() -> int { 2 | return sub(55, 55); 3 | } 4 | 5 | #[inline="always"] 6 | func sub(a: int, b: int) -> int { 7 | return a - b; 8 | } 9 | 10 | #[inline="never"] 11 | func never_inline() {} 12 | 13 | #[inline="maybe"] 14 | func maybe_inline() {} 15 | -------------------------------------------------------------------------------- /tests/func_type_mismatch.toml: -------------------------------------------------------------------------------- 1 | Name = "func_type_mismatch" 2 | Sourcefile = "func_type_mismatch.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 4 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/generic_inferrence.toml: -------------------------------------------------------------------------------- 1 | Name = "generic_inferrence" 2 | Sourcefile = "generic_inferrence.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/destructuring_assign.toml: -------------------------------------------------------------------------------- 1 | Name = "destructuring_assign" 2 | Sourcefile = "destructuring_assign.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = "" 14 | -------------------------------------------------------------------------------- /tests/nested_comment.ark: -------------------------------------------------------------------------------- 1 | /* We do lex 2 | /* nested comments */ 3 | /* Because 4 | /* Why not? */ 5 | // yup 6 | */ 7 | */ 8 | 9 | 10 | // thing 11 | 12 | 13 | /* hi */ 14 | 15 | pub func main() -> int { 16 | return 0; //hi /* yup */ 17 | } 18 | 19 | /* hi */ //thi 20 | -------------------------------------------------------------------------------- /tests/enum_union.toml: -------------------------------------------------------------------------------- 1 | Name = "enum_union" 2 | Sourcefile = "enum_union.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = """Node 14 | Leaf: 42 15 | Leaf: 36 16 | """ 17 | -------------------------------------------------------------------------------- /tests/fibonacci.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | func fib(n: int) -> int { 4 | if n < 2 { 5 | return n; 6 | } 7 | return fib(n - 1) + fib(n - 2); 8 | } 9 | 10 | pub func main() -> int { 11 | C::printf(c"fib(12) == %d\n", fib(12)); 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /tests/args.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...); 2 | 3 | pub func main(argc: int, argv: ^^u8) -> int { 4 | C::printf(c"%d\n", argc); 5 | 6 | mut i := 0; 7 | for i < argc { 8 | str := argv[i]; 9 | C::printf(c"%s\n", str); 10 | i += 1; 11 | } 12 | 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /tests/mutable_ref.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | func change_ref(x: &mut int) { 4 | @x = 21; 5 | } 6 | 7 | pub func main() -> int { 8 | mut x: int = 19; 9 | C::printf(c"x is %d\n", x); 10 | change_ref(&mut x); 11 | C::printf(c"x is %d\n", x); 12 | return 0; 13 | } 14 | -------------------------------------------------------------------------------- /tests/scanf_test.toml: -------------------------------------------------------------------------------- 1 | Name = "scanf_test" 2 | Sourcefile = "scanf_test.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "42\n" 11 | 12 | CompilerOutput = "" 13 | RunOutput = """Input a number: 14 | Your number: 42 15 | """ 16 | -------------------------------------------------------------------------------- /tests/variable_shadow.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | mut x := 0; 5 | 6 | if true { 7 | x = 1; 8 | mut x := 5; 9 | C::printf(c"x2: %d\n", x); 10 | } 11 | 12 | C::printf(c"x1: %d\n", x); 13 | 14 | return 0; 15 | } 16 | -------------------------------------------------------------------------------- /tests/call_conv.ark: -------------------------------------------------------------------------------- 1 | pub func main() -> int { 2 | return invoke(sub, 55, 55); 3 | } 4 | 5 | #[call_conv="x86fastcall"] 6 | func sub(a: int, b: int) -> int { 7 | return a - b; 8 | } 9 | 10 | func invoke(fn: #[call_conv="x86fastcall"] func(int, int) -> int, a: int, b: int) -> int { 11 | return fn(a, b); 12 | } 13 | -------------------------------------------------------------------------------- /tests/scanf_test.ark: -------------------------------------------------------------------------------- 1 | #[c] func scanf(fmt: ^u8, ...) -> int; 2 | #[c] func printf(fmt: ^u8, ...) -> int; 3 | 4 | pub func main() -> int { 5 | mut x: s32 = 0; 6 | 7 | C::printf(c"Input a number: "); 8 | C::scanf(c"%d", ^x); 9 | C::printf(c"\nYour number: %d\n", x); 10 | 11 | return 0; 12 | } 13 | -------------------------------------------------------------------------------- /tests/for_conditional.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | mut x := 2; 5 | mut y := 2; 6 | for x == 2 { 7 | y = x + y; 8 | if y > 100 { 9 | C::printf(c"%d\n", y); 10 | return 0; 11 | } 12 | } 13 | return 0; 14 | } 15 | -------------------------------------------------------------------------------- /tests/cast_call_ambiguity.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | glob: int = 5; 4 | 5 | func getGlob() -> ^int { 6 | return ^glob; 7 | } 8 | 9 | func printGlob() { 10 | C::printf(c"%d\n", @getGlob()); 11 | C::printf(c"%d\n", @(getGlob())); 12 | } 13 | 14 | pub func main() -> int { 15 | printGlob(); 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /tests/recursive.ark: -------------------------------------------------------------------------------- 1 | pub func main() -> int { 2 | do(0, 0); 3 | do2(0); 4 | 5 | return 0; 6 | } 7 | 8 | func do(i: int, t: T) { 9 | if i > 5 { 10 | return; 11 | } 12 | do(i + 1, t); 13 | } 14 | 15 | func do2(i: int) { 16 | if i > 5 { 17 | return; 18 | } 19 | do2(i + 1); 20 | } 21 | -------------------------------------------------------------------------------- /tests/stdlib/random.ark: -------------------------------------------------------------------------------- 1 | #use std::rand 2 | 3 | #[c] func printf(fmt: ^u8, ...); 4 | 5 | pub func main() -> int { 6 | mut it: rand::Pcg32Random; 7 | itp := ^mut it; 8 | itp.seed(123, 3); 9 | 10 | mut i := 0; 11 | for i < 20 { 12 | C::printf(c"%d ", itp.random()); 13 | i += 1; 14 | } 15 | C::printf(c"\n"); 16 | 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /tests/enum.ark: -------------------------------------------------------------------------------- 1 | type Test enum { 2 | VALUE, 3 | ANOTHER = 4, 4 | GG, 5 | }; 6 | 7 | pub func main() -> int { 8 | x := Test::VALUE; 9 | y := Test::ANOTHER; 10 | z := Test::GG; 11 | if Test::VALUE != x { 12 | return 1; 13 | } 14 | if Test::ANOTHER != y { 15 | return 2; 16 | } 17 | if Test::GG != z { 18 | return 3; 19 | } 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /tests/tuple.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | func something() -> (int, f32) { 4 | x := (4, 2.3); // inferred 5 | _ = x; 6 | y: (int, f32) = (0, 2.4); 7 | return y; 8 | } 9 | 10 | pub func main() -> int { 11 | x := something(); 12 | (y, z) := x; 13 | C::printf(c"%d %f\n", y, z); 14 | 15 | return y; 16 | } 17 | -------------------------------------------------------------------------------- /tests/bool.ark: -------------------------------------------------------------------------------- 1 | pub func main() -> int { 2 | trueTest := true; 3 | falseTest: bool = false; 4 | 5 | if trueTest == true { 6 | 7 | } 8 | 9 | if !falseTest == false { 10 | 11 | } 12 | 13 | if true != (1 == 1) { 14 | return 1; 15 | } 16 | 17 | if false != 4 > 6 { 18 | return 2; 19 | } 20 | 21 | return 0; 22 | } 23 | -------------------------------------------------------------------------------- /lib/Makefile: -------------------------------------------------------------------------------- 1 | flags := --unused 2 | entry_src := tests/test.ark 3 | stdlib_path := . 4 | 5 | test_srcs := $(wildcard tests/*.ark) 6 | tests := $(patsubst %.ark,%.test,$(wildcard tests/*.ark)) 7 | 8 | .PHONY: test clean 9 | 10 | %.test: %.ark 11 | ark build -I$(stdlib_path) -o $@ $< $(flags) $(debug) 12 | ./$@ 13 | 14 | test: clean $(tests) 15 | 16 | clean: 17 | @-rm $(tests) 18 | -------------------------------------------------------------------------------- /tests/ownership.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | type Test struct { 4 | a: int, 5 | }; 6 | 7 | func do_stuff(mut a: int) -> int { 8 | a = 21; 9 | return a; 10 | } 11 | 12 | pub func main() -> int { 13 | mut test: Test; 14 | test.a = 32; 15 | test.a = do_stuff(test.a); 16 | C::printf(c"a is %d\n", test.a); 17 | return 0; 18 | } 19 | -------------------------------------------------------------------------------- /tests/float_suffix.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | t1: f64 = 10f; 5 | t2: f64 = 0.4d; 6 | t3: f64 = 1214.935735q; 7 | _ = t3; 8 | 9 | t4: f64 = 10F; 10 | t5: f64 = 0.4D; 11 | t6: f64 = 1214.935735Q; 12 | _ = t6; 13 | 14 | C::printf(c"%f %f %f %f\n", t1, t2, t4, t5); 15 | 16 | return 0; 17 | } 18 | -------------------------------------------------------------------------------- /tests/nested_scope.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | mut x: int = 5; 5 | 6 | { 7 | mut x: f32 = 4.321; 8 | 9 | { 10 | mut x := c"foo"; 11 | C::printf(c"x = %s\n", x); 12 | } 13 | 14 | C::printf(c"x = %f\n", x); 15 | } 16 | 17 | C::printf(c"x = %d\n", x); 18 | 19 | return 0; 20 | } 21 | -------------------------------------------------------------------------------- /tests/pointer.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | func swap(a: ^mut T, b: ^mut T) 4 | { 5 | (@a, @b) = (@b, @a); 6 | } 7 | 8 | pub func main() -> int 9 | { 10 | a: s32 = 5; 11 | b: s32 = 6; 12 | C::printf(c"a: %d, b: %d\n", a, b); 13 | swap(^mut a, ^mut b); 14 | C::printf(c"a: %d, b: %d\n", a, b); 15 | 16 | return 6 - int(a); // Not inference problems ugh 17 | } 18 | -------------------------------------------------------------------------------- /lib/tests/test-stack.ark: -------------------------------------------------------------------------------- 1 | #use std::adt 2 | #use std::io 3 | 4 | pub func main() -> int { 5 | stack := adt::Stack::new(); 6 | defer stack.destroy(); 7 | stack.push(5); 8 | stack.push(10); 9 | 10 | mut idx := uint(0); 11 | for idx < stack.getLength() { 12 | value: Option = stack.pop(); 13 | actualValue := value.unwrap(); 14 | io::printInt(actualValue); 15 | io::printRune('\n'); 16 | } 17 | 18 | return 0; 19 | } -------------------------------------------------------------------------------- /lib/CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing 2 | There are a few ways to contribute: 3 | 4 | * Fixing bugs in the issue tracker 5 | * Discussing features on the IRC channel (#ark-lang on irc.freenode.net) 6 | * Improving code in the standard library 7 | * Finding bugs and creating issues for them in the issue handler 8 | 9 | ## Style Guide 10 | If you're contributing code to the standard library, please follow the 11 | style guide [here](/STYLEGUIDE.md). -------------------------------------------------------------------------------- /tests/stdform_nums.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...); 2 | 3 | pub func main() -> int { 4 | if 2e3 != 2000 { 5 | return 1; 6 | } 7 | 8 | if 2E3 != 2000 { 9 | return 2; 10 | } 11 | 12 | if 2000e-3 != 2 { 13 | return 3; 14 | } 15 | 16 | if 1.5e4 != 15000 { 17 | return 4; 18 | } 19 | 20 | if 0.5e-2 != 0.005 { 21 | return 5; 22 | } 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/stdlib/random.toml: -------------------------------------------------------------------------------- 1 | Name = "random" 2 | Sourcefile = "random.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = """1998954086 866830645 1635602452 1328654512 -1586894466 195079025 989917749 728366270 -43827515 219593773 1341085415 39457914 -1147313730 2009496333 -1454918132 -1904258022 -1819006468 821169792 -220037324 1453362602 14 | """ -------------------------------------------------------------------------------- /tests/variable_zeroing.ark: -------------------------------------------------------------------------------- 1 | mut globZeroedVariable: int; 2 | 3 | type Thing struct { 4 | x: u32, 5 | y: s64, 6 | }; 7 | 8 | pub func main() -> int { 9 | if globZeroedVariable != 0 { 10 | return 1; 11 | } 12 | 13 | #[nozero] mut dirtyVariable: int; 14 | _ = dirtyVariable; 15 | mut zeroedVariable: int; 16 | if zeroedVariable != 0 { 17 | return 2; 18 | } 19 | 20 | mut thing: Thing; 21 | if thing.x != 0 || thing.y != 0 { 22 | return 3; 23 | } 24 | 25 | return 0; 26 | } -------------------------------------------------------------------------------- /tests/deref.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | mut x: s32 = 9; 5 | y: ^s32 = ^x; 6 | 7 | C::printf(c"%d\n", @y); 8 | 9 | x = 15; 10 | z: ^^s32 = ^y; 11 | 12 | C::printf(c"%d\n", @@z); 13 | 14 | x = 9000; 15 | 16 | a: ^^^s32 = ^z; 17 | b: ^^^^s32 = ^a; 18 | c: ^^^^^s32 = ^b; 19 | d: ^^^^^^s32 = ^c; 20 | e: ^^^^^^^s32 = ^d; 21 | 22 | C::printf(c"%d\n", @@@@@@@e); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/single_stat_func.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | func add(a: int, b: int) -> int => a + b; 4 | 5 | func fuck(mut a: int, b: int) => for a + b >= 32 { 6 | a -= 1; 7 | } 8 | 9 | func test(mut a: int) => for a < 10 { 10 | C::printf(c"this is a test %d\n", a); 11 | a = a + 1; 12 | } 13 | 14 | func whatever() => C::printf(c"testing the whatever() func\n"); 15 | 16 | pub func main() -> int { 17 | whatever(); 18 | z: int = 0; 19 | test(z); 20 | return 0; 21 | } 22 | -------------------------------------------------------------------------------- /src/parser/unoptype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=UnOpType"; DO NOT EDIT 2 | 3 | package parser 4 | 5 | import "fmt" 6 | 7 | const _UnOpType_name = "UNOP_ERRUNOP_LOG_NOTUNOP_BIT_NOTUNOP_NEGATIVEUNOP_DEREF" 8 | 9 | var _UnOpType_index = [...]uint8{0, 8, 20, 32, 45, 55} 10 | 11 | func (i UnOpType) String() string { 12 | if i < 0 || i >= UnOpType(len(_UnOpType_index)-1) { 13 | return fmt.Sprintf("UnOpType(%d)", i) 14 | } 15 | return _UnOpType_name[_UnOpType_index[i]:_UnOpType_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /tests/generic_member.ark: -------------------------------------------------------------------------------- 1 | 2 | type Cell struct { 3 | data: T, 4 | }; 5 | 6 | func (Cell) new(val: T) -> Cell { 7 | res := Cell{data: val}; 8 | return res; 9 | } 10 | 11 | func (this: Cell) get() -> T { 12 | return this.data; 13 | } 14 | 15 | func (mut this: Cell) set(val: T) { 16 | this.data = val; 17 | } 18 | 19 | pub func main() -> int { 20 | a := Cell::new(13); 21 | 22 | // I am fully aware that this doesn't actually do anything 23 | a.set(42); 24 | 25 | return 13 - a.get(); 26 | } -------------------------------------------------------------------------------- /lib/std/adt/linkedlist.ark: -------------------------------------------------------------------------------- 1 | /* 2 | 3 | BLOCKED BY #724 4 | 5 | #use std::mem 6 | 7 | pub type Node struct { 8 | data: T, 9 | prev: ^mut Node, 10 | next: ^mut Node, 11 | }; 12 | 13 | pub type LinkedList struct { 14 | head: ^mut Node, 15 | }; 16 | 17 | pub func (LinkedList) new() -> ^mut LinkedList { 18 | list := mem::alloc>(); 19 | @list = LinkedList { 20 | }; 21 | return list; 22 | } 23 | 24 | pub func (l: ^LinkedList) destroy() { 25 | mem::free(l); 26 | } 27 | */ -------------------------------------------------------------------------------- /tests/float_ops.ark: -------------------------------------------------------------------------------- 1 | func doStuff(a: f32) { 2 | } 3 | 4 | // use this to prevent LLVM constant-folding away numbers 5 | func get5() -> f32 { 6 | return 5; 7 | } 8 | 9 | pub func main() -> int { 10 | mut foo: f32 = 42.0; 11 | doStuff(-120); 12 | doStuff(-foo); 13 | 14 | foo = foo / 24; 15 | 16 | if get5() / 5 != 1 { 17 | return 1; 18 | } 19 | 20 | if get5() + 10 != 15 { 21 | return 2; 22 | } 23 | 24 | if get5() * 10 != 50 { 25 | return 3; 26 | } 27 | 28 | if get5() - 3 != 2 { 29 | return 4; 30 | } 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/option.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | 3 | func debugDumpOption(o: Option) { 4 | match o { 5 | Some(_) => io::println("Option::Some"), 6 | None => io::println("Option::None"), 7 | } 8 | } 9 | 10 | pub func main() -> int { 11 | io::println("Option test"); 12 | 13 | some := Option::Some(5); 14 | none := Option::None(); 15 | 16 | debugDumpOption(some); 17 | debugDumpOption(none); 18 | debugDumpOption(Option::Some(5)); 19 | 20 | io::printInt(some.unwrap()); 21 | io::println(""); 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /examples/factorial.ark: -------------------------------------------------------------------------------- 1 | [c] func printf(fmt: ^u8, ...) -> int; 2 | [c] func scanf(fmt: ^u8, ...); 3 | 4 | func factorial(n: int) -> int { 5 | if n == 0 { 6 | return 1; 7 | } 8 | return n * factorial(n - 1); 9 | } 10 | 11 | pub func main() -> int { 12 | number: int = 1; 13 | C::printf(c"Enter a number: \n"); 14 | C::scanf("%d", &number); 15 | 16 | if number < 0 { 17 | C::printf(c"No negatives please\n"); 18 | return -1; 19 | } 20 | 21 | result := factorial(number); 22 | C::printf(c"%d! = %d\n", number, result); 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /tests/binop_assign.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | mut glob: int = 5; 4 | 5 | func getGlob() -> ^mut int { 6 | glob += 1; 7 | return ^mut glob; 8 | } 9 | 10 | pub func main() -> int { 11 | mut x := 0; 12 | x += 5; 13 | 14 | C::printf(c"x was `0`, became `%d`\n", x); 15 | if x != 5 { 16 | return 1; 17 | } 18 | 19 | x <<= 2; 20 | 21 | C::printf(c"x was `5`, became `%d`\n", x); 22 | if x != 20 { 23 | return 2; 24 | } 25 | 26 | @getGlob() += 0; 27 | 28 | C::printf(c"glob is `%d`\n", glob); 29 | if glob != 6 { 30 | return 3; 31 | } 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /tests/c_ints.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | #[c] func malloc(size: uint) -> ^C::void; 3 | #[c] func free(ptr: ^C::void); 4 | 5 | pub func main() -> int { 6 | x: C::uint = 0; 7 | y: C::int = 0; 8 | z: uint = 0; 9 | w: int = 0; 10 | 11 | C::printf(c"sizeof(C::uint) = %d\n", sizeof(x)); 12 | if sizeof(x) != 4 { 13 | return 1; 14 | } 15 | 16 | C::printf(c"sizeof(C::int) = %d\n", sizeof(y)); 17 | if sizeof(y) != 4 { 18 | return 2; 19 | } 20 | 21 | C::printf(c"sizeof(uint) = %d\n", sizeof(z)); 22 | C::printf(c"sizeof(int) = %d\n", sizeof(w)); 23 | 24 | return 0; 25 | } 26 | -------------------------------------------------------------------------------- /tests/match.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | 3 | #[c] func printf(fmt: ^u8, ...) -> int; 4 | 5 | pub func main() -> int { 6 | valid: bool = true; 7 | match valid { 8 | true => io::println("true"), 9 | false => io::println("false"), 10 | } 11 | 12 | foo := "bar"; 13 | match foo { 14 | "hello" => C::printf(c"foo matches hello"), 15 | "world" => { }, 16 | "bar" => { 17 | award := c"baz"; 18 | C::printf(c"correct! you win %s\n", award); 19 | }, 20 | _ => io::println("default"), 21 | } 22 | 23 | return 0; 24 | } 25 | -------------------------------------------------------------------------------- /lib/tests/test-string.ark: -------------------------------------------------------------------------------- 1 | #use std::string 2 | #use std::io 3 | 4 | // todo non-shit test 5 | 6 | pub func main() -> int { 7 | a := "foo"; 8 | b := "bar"; 9 | something := "this is some kind of string!"; 10 | 11 | if string::hasPrefix(a, "fo") { 12 | io::println("foo has the prefix fo!"); 13 | } 14 | if string::hasSuffix("test.ark", ".ark") { 15 | io::println("test.ark has the suffix .ark"); 16 | } 17 | if string::hasSuffix(b, "bar") { 18 | io::println("nice"); 19 | } 20 | 21 | if string::contains(something, "kind") { 22 | io::println("nice..."); 23 | } 24 | 25 | if string::compare(a, b) { 26 | return -1; 27 | } 28 | return 0; 29 | } -------------------------------------------------------------------------------- /tests/fizzbuzz.ark: -------------------------------------------------------------------------------- 1 | // Fizzbuzz 2 | // %3 -> Fizz 3 | // %5 -> Buzz 4 | 5 | #[c] func printf(fmt: ^u8, ...) -> int; 6 | #[c] func putchar(c: s8); 7 | 8 | pub func main() -> int { 9 | mut i := 1; 10 | 11 | for i <= 100 { 12 | div3 := i % 3 == 0; 13 | div5 := i % 5 == 0; 14 | 15 | if div3 { 16 | C::printf(c"Fizz"); 17 | } 18 | 19 | if div5 { 20 | C::printf(c"Buzz"); 21 | } 22 | 23 | if !(div3 || div5) { 24 | C::printf(c"%d", i); 25 | } 26 | 27 | C::putchar(s8('\n')); 28 | 29 | i += 1; 30 | } 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /src/ast/runtime.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/util/log" 5 | ) 6 | 7 | func LoadRuntimeModule(mod *Module) { 8 | for name, ident := range mod.ModScope.Idents { 9 | if ident.Public { 10 | builtinScope.InsertIdent(ident.Value, name, ident.Type, ident.Public) 11 | } 12 | } 13 | } 14 | 15 | func runtimeMustLoadType(mod *Module, name string) Type { 16 | log.Debugln("runtime", "Loading runtime type: %s", name) 17 | ident := mod.ModScope.GetIdent(UnresolvedName{Name: name}) 18 | if ident.Type != IDENT_TYPE { 19 | panic("INTERNAL ERROR: Type not defined in runtime: " + name) 20 | } 21 | return ident.Value.(Type) 22 | } 23 | -------------------------------------------------------------------------------- /tests/func_pointer.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | func do(thing: int) { 4 | C::printf(c"%d\n", thing); 5 | } 6 | 7 | pub func main() -> int { 8 | C::printf(c"Func ptr test\n"); 9 | 10 | fn := func() -> int { 11 | return 5; 12 | }; 13 | 14 | another := func() -> int => return 6; 15 | 16 | mut dofn: func(int); 17 | dofn = do; 18 | 19 | dofn2 := do; 20 | _ = dofn2; 21 | 22 | dofn(fn()); 23 | dofn(another()); 24 | 25 | runFunc(func() { 26 | C::printf(c"Printing from a lambda!\n"); 27 | }); 28 | 29 | return 0; 30 | } 31 | 32 | func runFunc(fn: func()) { 33 | fn(); 34 | } 35 | -------------------------------------------------------------------------------- /tests/enum_union.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | type Tree enum { 4 | Node{left: ^Tree, right: ^Tree}, 5 | Leaf(int) 6 | }; 7 | 8 | func (tree: ^Tree) dump() { 9 | match @tree { 10 | Node(left, right) => { 11 | C::printf(c"Node\n"); 12 | left.dump(); 13 | right.dump(); 14 | }, 15 | Leaf(value) => { 16 | C::printf(c"Leaf: %d\n", value); 17 | }, 18 | _ => { 19 | C::printf(c"Invalid tree instance\n"); 20 | }, 21 | } 22 | } 23 | 24 | x := Tree::Leaf(42); 25 | 26 | pub func main() -> int { 27 | y := Tree::Leaf(36); 28 | z := Tree::Node{left: ^x, right: ^y}; 29 | 30 | zp := ^z; 31 | zp.dump(); 32 | 33 | return 0; 34 | } 35 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | # TODO: Recategorise this 2 | *.a 3 | *.app 4 | *.bc 5 | *.cgo1.go 6 | *.cgo2.c 7 | *.dll 8 | *.dSYM 9 | *.dylib 10 | *.elf 11 | *.exe 12 | *.gch 13 | *.hex 14 | *.i*86 15 | *.ko 16 | *.la 17 | *.lib 18 | *.ll 19 | *.lo 20 | *.o 21 | *.obj 22 | *.out 23 | *.pch 24 | *.prof 25 | *.s 26 | *.so 27 | *.so.* 28 | *.stackdump 29 | *.swo 30 | *.swp 31 | *.test 32 | *.x86_64 33 | *.[568vq] 34 | *sublime-* 35 | *~ 36 | .cproject 37 | .dSYM/ 38 | .DS_Store 39 | .floo 40 | .flooignore 41 | .project 42 | .settings 43 | ark_run_tmp 44 | bin/ 45 | builds/ 46 | docgen/ 47 | local/ 48 | main 49 | \#*\# 50 | _cgo_defun.c 51 | _cgo_export.* 52 | _cgo_gotypes.go 53 | _gen_*.c 54 | _gen_*.h 55 | _obj 56 | _test 57 | _testmain.go 58 | -------------------------------------------------------------------------------- /tests/stdlib/list.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | #use std::adt 3 | 4 | func printIntList(list: ^adt::List) { 5 | io::print("["); 6 | 7 | mut i: uint = 0; 8 | length := list.getLength(); 9 | for i < length { 10 | io::printInt(list.get(i)); 11 | 12 | if i < length - 1 { 13 | io::print(", "); 14 | } 15 | 16 | i += 1; 17 | } 18 | 19 | io::println("]"); 20 | } 21 | 22 | pub func main() -> int { 23 | io::println("list test"); 24 | 25 | list := adt::List::new(); 26 | list.append(5); 27 | 28 | io::printInt(list.get(0)); 29 | io::println(""); 30 | 31 | list.append(10); 32 | 33 | printIntList(list); 34 | 35 | return 0; 36 | } 37 | -------------------------------------------------------------------------------- /src/parser/binoptype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=BinOpType"; DO NOT EDIT 2 | 3 | package parser 4 | 5 | import "fmt" 6 | 7 | const _BinOpType_name = "BINOP_ERRBINOP_ADDBINOP_SUBBINOP_MULBINOP_DIVBINOP_MODBINOP_GREATERBINOP_LESSBINOP_GREATER_EQBINOP_LESS_EQBINOP_EQBINOP_NOT_EQBINOP_BIT_ANDBINOP_BIT_ORBINOP_BIT_XORBINOP_BIT_LEFTBINOP_BIT_RIGHTBINOP_LOG_ANDBINOP_LOG_OR" 8 | 9 | var _BinOpType_index = [...]uint8{0, 9, 18, 27, 36, 45, 54, 67, 77, 93, 106, 114, 126, 139, 151, 164, 178, 193, 206, 218} 10 | 11 | func (i BinOpType) String() string { 12 | if i < 0 || i >= BinOpType(len(_BinOpType_index)-1) { 13 | return fmt.Sprintf("BinOpType(%d)", i) 14 | } 15 | return _BinOpType_name[_BinOpType_index[i]:_BinOpType_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /tests/interface.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | 3 | // TODO: make sure interfaces play nice with methods on generic types, eg: 4 | // func (v: Stack) do() { 5 | // io::print_int(sizeof(T)); 6 | // } 7 | 8 | type IDoer interface { 9 | func do(), 10 | }; 11 | 12 | type Doer1 struct {}; 13 | 14 | func (v: Doer1) do() { 15 | io::println("did1!"); 16 | } 17 | 18 | type Doer2 struct {}; 19 | 20 | func (v: Doer2) do() { 21 | io::println("did2!"); 22 | } 23 | 24 | func runDoer(t: T) { 25 | t.do(); 26 | } 27 | 28 | pub func main() -> int { 29 | io::println("interface test"); 30 | 31 | doer1 := Doer1{}; 32 | doer2 := Doer2{}; 33 | 34 | runDoer(doer1); 35 | runDoer(doer2); 36 | 37 | return 0; 38 | } 39 | -------------------------------------------------------------------------------- /lib/std/adt/hashmap.ark: -------------------------------------------------------------------------------- 1 | #use std::mem 2 | 3 | // BASE_CAPACITY: uint = 32; 4 | 5 | pub type Element struct { 6 | key: K, 7 | val: V, 8 | busy: bool, 9 | }; 10 | 11 | pub type HashMap struct { 12 | data: []^Element, 13 | capacity: uint, 14 | size: uint, 15 | }; 16 | 17 | pub func (HashMap) new() -> ^mut HashMap { 18 | map := mem::alloc>(); 19 | cap: uint = BASE_CAPACITY; 20 | @map = HashMap { 21 | size: 0, 22 | capacity: cap, 23 | }; 24 | return map; 25 | } 26 | 27 | pub func (h: ^HashMap) get(key: K) -> Option { 28 | 29 | return Option::None; 30 | } 31 | 32 | pub func (h: ^HashMap) put(key: K, val: V) { 33 | 34 | } 35 | 36 | pub func (h: ^HashMap) destroy() { 37 | 38 | } -------------------------------------------------------------------------------- /tests/arrays/array_len.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | C::printf(c"this is a test\n"); 5 | 6 | // iterates 15 times 7 | z : uint = len([]int{0, 1, 2}) + 12; 8 | mut i: uint = 0; 9 | for i < z { 10 | C::printf(c"iter %d\n", i); 11 | i += 1; 12 | } 13 | 14 | // iterate thru array 15 | // this doesn't work as it should yet... 16 | foo := []int{0, 1, 2, 3}; 17 | 18 | // i haven't looked into it 19 | // but #foo seems to just be 20 | // a copy of the foo array or something? ... 21 | fooLen := len(foo); 22 | C::printf(c"foo is %d long\n", fooLen); 23 | 24 | i = 0; 25 | for i < len(foo) { 26 | C::printf(c"foo contains %d at %d\n", foo[i], i); 27 | i += 1; 28 | } 29 | 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /tests/sizeof.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | type Test struct { 4 | x: int, 5 | y: int, 6 | a: int, 7 | b: int 8 | }; 9 | 10 | pub func main() -> int { 11 | i: int = 0; 12 | C::printf(c"sizeof i: %d\n", sizeof(i)); 13 | C::printf(c"sizeof &i: %d\n", sizeof(&i)); 14 | 15 | a: u8 = 0; 16 | C::printf(c"sizeof a: %d\n", sizeof(a)); 17 | 18 | mut test: Test; 19 | C::printf(c"sizeof test: %d\n", sizeof(test)); 20 | 21 | x: u8 = 0; 22 | if sizeof(x) != 1 { 23 | return 1; 24 | } 25 | 26 | y: s16 = 0; 27 | if sizeof(y) != 2 { 28 | return 2; 29 | } 30 | 31 | z: s64 = 0; 32 | if sizeof(z) != 8 { 33 | return 3; 34 | } 35 | 36 | return 0; 37 | } 38 | -------------------------------------------------------------------------------- /tests/for.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | mut i := 0; 5 | 6 | for i < 5 { 7 | C::printf(c"i: %d\n", i); 8 | i = i + 1; 9 | } 10 | 11 | mut x := 0; 12 | 13 | for x < 10 { 14 | C::printf(c"x: %d\n", x); 15 | 16 | x += 1; 17 | 18 | for { 19 | break; 20 | } 21 | 22 | if x == 5 { 23 | break; 24 | } else { 25 | C::printf(c"else\n"); 26 | } 27 | } 28 | 29 | mut y := 0; 30 | for y <= 100 { 31 | C::printf(c"y: %d\n", y); 32 | y += 1; 33 | if y < 5 { 34 | next; 35 | } else { 36 | break; 37 | } 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /src/doc/markdown.go: -------------------------------------------------------------------------------- 1 | package doc 2 | 3 | import ( 4 | "html/template" 5 | 6 | "github.com/russross/blackfriday" 7 | ) 8 | 9 | var ( 10 | mdRendererFlags = blackfriday.HTML_SKIP_HTML | blackfriday.HTML_SKIP_STYLE | blackfriday.HTML_SKIP_IMAGES | blackfriday.HTML_NOREFERRER_LINKS | blackfriday.HTML_SAFELINK | blackfriday.HTML_USE_XHTML 11 | mdRenderer = blackfriday.HtmlRenderer(mdRendererFlags, "", "") 12 | mdOptions = blackfriday.Options{Extensions: blackfriday.EXTENSION_STRIKETHROUGH | blackfriday.EXTENSION_HARD_LINE_BREAK} 13 | ) 14 | 15 | func parseMarkdown(input string) string { 16 | if input == "" { 17 | return "" 18 | } 19 | 20 | ret := blackfriday.MarkdownOptions([]byte(template.HTMLEscapeString(input)), mdRenderer, mdOptions) 21 | 22 | return string(ret) 23 | } 24 | -------------------------------------------------------------------------------- /src/ast/primitivetype_string.go: -------------------------------------------------------------------------------- 1 | // Code generated by "stringer -type=PrimitiveType"; DO NOT EDIT 2 | 3 | package ast 4 | 5 | import "fmt" 6 | 7 | const _PrimitiveType_name = "PRIMITIVE_s8PRIMITIVE_s16PRIMITIVE_s32PRIMITIVE_s64PRIMITIVE_s128PRIMITIVE_u8PRIMITIVE_u16PRIMITIVE_u32PRIMITIVE_u64PRIMITIVE_u128PRIMITIVE_f32PRIMITIVE_f64PRIMITIVE_f128PRIMITIVE_intPRIMITIVE_uintPRIMITIVE_uintptrPRIMITIVE_boolPRIMITIVE_void" 8 | 9 | var _PrimitiveType_index = [...]uint8{0, 12, 25, 38, 51, 65, 77, 90, 103, 116, 130, 143, 156, 170, 183, 197, 214, 228, 242} 10 | 11 | func (i PrimitiveType) String() string { 12 | if i < 0 || i >= PrimitiveType(len(_PrimitiveType_index)-1) { 13 | return fmt.Sprintf("PrimitiveType(%d)", i) 14 | } 15 | return _PrimitiveType_name[_PrimitiveType_index[i]:_PrimitiveType_index[i+1]] 16 | } 17 | -------------------------------------------------------------------------------- /src/codegen/codegen.go: -------------------------------------------------------------------------------- 1 | package codegen 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ark-lang/ark/src/ast" 7 | ) 8 | 9 | type Codegen interface { 10 | Generate(input []*ast.Module) 11 | } 12 | 13 | type OutputType int 14 | 15 | const ( 16 | OutputUnknown OutputType = iota 17 | OutputExectuably 18 | OutputObject 19 | OutputAssembly 20 | OutputLLVMIR 21 | ) 22 | 23 | var typeMapping = map[string]OutputType{ 24 | "executable": OutputExectuably, 25 | "object": OutputObject, 26 | "assembly": OutputAssembly, 27 | "llvm-ir": OutputLLVMIR, 28 | } 29 | 30 | func ParseOutputType(input string) (OutputType, error) { 31 | typ, ok := typeMapping[input] 32 | if !ok { 33 | return OutputUnknown, fmt.Errorf("ark-lang/codegen: Unknown output type `%s`", input) 34 | } 35 | return typ, nil 36 | } 37 | -------------------------------------------------------------------------------- /tests/arrays/array_global.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | mut arrayGlobalTest: []s16 = []s16{100, 101, 102, 103, 104}; 4 | random := []int{0, 16, 32, 64, 128, 256, 512, 1024}; 5 | 6 | pub func main() -> int { 7 | if arrayGlobalTest[1] != 101 { 8 | return 1; 9 | } 10 | 11 | if arrayGlobalTest[2] != 102 { 12 | return 2; 13 | } 14 | 15 | arrayGlobalTest[3] = 9999; 16 | if arrayGlobalTest[3] != 9999 { 17 | return 3; 18 | } 19 | 20 | mut i := 0; 21 | for i < 5 { 22 | C::printf(c"array index %d contains %d\n", i, arrayGlobalTest[i]); 23 | i = i + 1; 24 | } 25 | 26 | i = 0; 27 | for i < 8 { 28 | C::printf(c"random contains %d at index %d\n", random[i], i); 29 | i = i + 1; 30 | } 31 | 32 | return 0; 33 | } 34 | -------------------------------------------------------------------------------- /tests/cast.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | type intArray []int; 4 | 5 | pub func main() -> int { 6 | a: int = 0; 7 | C::printf(c"%f\n", f32(a)); 8 | 9 | b: f32 = 6.789; 10 | C::printf(c"%d\n", int(b)); 11 | 12 | c: s8 = 66; 13 | C::printf(c"%d\n", s32(c)); 14 | 15 | d: s32 = 500; 16 | C::printf(c"%d\n", s8(d)); 17 | 18 | e: f64 = 1.2; 19 | C::printf(c"%f\n", f32(e)); 20 | 21 | f: rune = 'f'; 22 | C::printf(c"%d\n", int(f)); 23 | if int(f) != 102 { 24 | return 1; 25 | } 26 | 27 | g: int = 99; 28 | C::printf(c"%c\n", rune(g)); 29 | if rune(g) != 'c' { 30 | return 2; 31 | } 32 | 33 | h := []int{1, 2, 3, 4}; 34 | i := intArray(h); 35 | j := []int(i); 36 | C::printf(c"%d\n", j[2]); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /lib/tests/test-dynamic-string.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | #use std::strings 3 | 4 | pub func main() -> int { 5 | test := strings::String::from("hello "); 6 | defer test.destroy(); 7 | 8 | io::printDynamicString(test); 9 | 10 | test.append('w'); 11 | test.append('o'); 12 | test.concat("rld"); 13 | io::printDynamicString(test); 14 | 15 | poppedValue := test.pop(); 16 | 17 | io::println("just popped: "); 18 | io::printRune(poppedValue.unwrap()); 19 | io::println(""); 20 | 21 | io::printDynamicString(test); 22 | 23 | mut idx := uint(0); 24 | io::printUint(test.length()); 25 | for idx < test.length() { 26 | poppedValue := test.pop(); 27 | io::printRune(poppedValue.unwrap()); 28 | idx += 1; 29 | } 30 | 31 | test.append('京'); 32 | value := test.pop(); 33 | 34 | io::printRune('\n'); 35 | io::printRune(value.unwrap()); 36 | 37 | return 0; 38 | } -------------------------------------------------------------------------------- /lib/std/mem/mem.ark: -------------------------------------------------------------------------------- 1 | // TODO: check for overflow when doing `num * sizeof(T)`` 2 | 3 | pub func alloc() -> ^mut T { 4 | return (^mut T)(rawAlloc(sizeof(T))); 5 | } 6 | 7 | pub func allocArray(num: uint) -> []T { 8 | rawPtr := (^T)(rawAlloc(sizeof(T) * num)); 9 | return makeArray(rawPtr, num); 10 | } 11 | 12 | pub func copyArray(mut dest: []T, src: []T, num: uint) { 13 | mut max := num; 14 | if max > len(dest) { 15 | max = len(dest); 16 | } 17 | if max > len(src) { 18 | max = len(src); 19 | } 20 | 21 | mut i: uint = 0; 22 | for i < max { 23 | dest[i] = src[i]; 24 | i += 1; 25 | } 26 | } 27 | 28 | pub func free(ptr: ^T) { 29 | rawFree(uintptr(ptr)); 30 | } 31 | 32 | pub func freeArray(array: []T) { 33 | (_, ptr) := breakArray(array); 34 | rawFree(uintptr(ptr)); 35 | } -------------------------------------------------------------------------------- /tests/func.ark: -------------------------------------------------------------------------------- 1 | func add(a: int, b: int) -> int { 2 | return a + b; 3 | } 4 | 5 | func thisIsVoid(swag: int) { 6 | x := swag; 7 | _ = x; 8 | } 9 | 10 | pub func main() -> int { 11 | x: int = 5; 12 | z: int = add(5, 10); 13 | y: int = 19 - 25; 14 | runeTest := 'h'; 15 | floatTest := 12.34; 16 | 17 | _ = (x, y, z, runeTest, floatTest); 18 | 19 | thisIsVoid(5); 20 | 21 | if 1 == 1 { 22 | if 5 == 5 { 23 | second1 := 0; 24 | _ = second1; 25 | } else { 26 | second2 := 1; 27 | _ = second2; 28 | } 29 | } else if 1 == 0 { 30 | duh2: int = 6; 31 | _ = duh2; 32 | } else if 1 == 6 { 33 | duh3: int = 7; 34 | _ = duh3; 35 | } else { 36 | duh4: int = 8; 37 | _ = duh4; 38 | } 39 | 40 | return 0; 41 | } 42 | -------------------------------------------------------------------------------- /examples/higher_or_lower.ark: -------------------------------------------------------------------------------- 1 | [c] func printf(fmt: ^u8, ...); 2 | [c] func rand() -> int; 3 | [c] func srand(i: uint); 4 | [c] func time(ptr: u32) -> u64; 5 | [c] func scanf(fmt: ^u8, ...); 6 | 7 | func println(mess: ^u8) => C::printf(c"%s\n", mess); 8 | 9 | pub func main() -> int { 10 | println("Higher or lower game, in Ark!"); 11 | 12 | C::srand(uint(C::time(0))); 13 | 14 | num := C::rand() % 100 + 1; 15 | 16 | for { 17 | println("\nEnter a number between 1 and 100 (inclusive):"); 18 | mut guess: int; 19 | C::scanf("%d", &guess); 20 | 21 | if guess == num { 22 | println("You win!"); 23 | return 0; 24 | } else if guess < num { 25 | println("Guess higher!"); 26 | } else { 27 | println("Guess lower!"); 28 | } 29 | } 30 | 31 | return 0; 32 | } 33 | -------------------------------------------------------------------------------- /src/util/log/timed.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "strings" 5 | "time" 6 | 7 | "github.com/ark-lang/ark/src/util" 8 | ) 9 | 10 | // magic 11 | var indent int = 0 12 | 13 | func Timed(titleColored, titleUncolored string, fn func()) { 14 | var bold string 15 | if indent == 0 { 16 | bold = util.TEXT_BOLD 17 | } 18 | 19 | if titleUncolored != "" { 20 | titleUncolored = " " + titleUncolored 21 | } 22 | 23 | Verbose("main", strings.Repeat(" ", indent)) 24 | Verboseln("main", bold+util.TEXT_GREEN+"Started "+titleColored+util.TEXT_RESET+titleUncolored) 25 | start := time.Now() 26 | 27 | indent++ 28 | fn() 29 | indent-- 30 | 31 | duration := time.Since(start) 32 | Verbose("main", strings.Repeat(" ", indent)) 33 | Verboseln("main", bold+util.TEXT_GREEN+"Ended "+titleColored+util.TEXT_RESET+titleUncolored+" (%.2fms)", float32(duration)/1000000) 34 | } 35 | -------------------------------------------------------------------------------- /tests/defer.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | 3 | #[c] func printf(fmt: ^u8, ...) -> int; 4 | 5 | func do() { 6 | defer io::println("do(): printed last"); 7 | 8 | if true { 9 | defer io::println("do(): printed second-to-last"); 10 | return; 11 | } 12 | } 13 | 14 | pub func main() -> int { 15 | defer io::println("printed last"); 16 | defer io::println("printed second-to-last"); 17 | 18 | if true { 19 | defer io::println("printed second"); 20 | defer io::println("printed first"); 21 | } 22 | 23 | do { 24 | defer io::println("print"); 25 | defer io::println("print"); 26 | } 27 | 28 | mut i := 0; 29 | for i < 5 { 30 | defer C::printf(c"i: %d\n", i); 31 | defer io::println("Loop iteration..."); 32 | 33 | i += 1; 34 | } 35 | 36 | do(); 37 | 38 | return 0; 39 | } 40 | -------------------------------------------------------------------------------- /tests/iterator.ark: -------------------------------------------------------------------------------- 1 | //#use std::io 2 | 3 | #[c] func printf(fmt: ^u8, ...) -> int; 4 | 5 | type Iterator interface { 6 | func iterate() -> Option, 7 | }; 8 | 9 | type Test struct { 10 | i: int, 11 | }; 12 | 13 | func (t: ^mut Test) iterate() -> Option { 14 | if t.i > 0 { 15 | t.i -= 1; 16 | return Option::Some(t.i); 17 | } 18 | return Option::None; 19 | } 20 | 21 | func iterateOver>(t: T) { 22 | for { 23 | i := t.iterate(); 24 | match i { 25 | Some(val) => { 26 | v: int = val; 27 | //io::printInt(val); io::println(""); 28 | }, 29 | None => return, 30 | } 31 | } 32 | } 33 | 34 | pub func main() -> int { 35 | //io::println("iter"); 36 | 37 | test := Test{i: 5}; 38 | 39 | iterateOver(^mut test); 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /lib/tests/test-fileio.ark: -------------------------------------------------------------------------------- 1 | #use std::mem 2 | #use std::io 3 | #use std::strings 4 | 5 | pub func main(argc: int, argv: ^^u8) -> int { 6 | mut fileToRead: ^u8; 7 | if argc < 2 { 8 | fileToRead = c"../LICENSE"; 9 | } else { 10 | fileToRead = argv[1]; 11 | } 12 | 13 | maybeFile: Option<^mut io::File> = io::File::open(fileToRead, io::FileMode::Read); 14 | 15 | mut myFile: ^mut io::File; 16 | 17 | match maybeFile { 18 | Some(t) => myFile = t, 19 | None => { 20 | io::println("Error! Failed to read file"); 21 | return -1; 22 | }, 23 | } 24 | 25 | result := myFile.readToString(); 26 | match result { 27 | Some(s) => { 28 | io::printDynamicString(s); 29 | }, 30 | None => { 31 | io::println("Failed to print file contents!"); 32 | return -1; 33 | }, 34 | } 35 | 36 | myFile.destroy(); 37 | 38 | return 0; 39 | } -------------------------------------------------------------------------------- /tests/struct.ark: -------------------------------------------------------------------------------- 1 | type EmptyStruct struct {}; 2 | 3 | #[c] func printf(fmt: ^u8, ...) -> int; 4 | 5 | mut thing := Test{z: 42}; 6 | 7 | pub func main() -> int { 8 | C::printf(c"thing.z: %f\n", thing.z); 9 | if thing.z != 42 { 10 | return 1; 11 | } 12 | 13 | thing.y = 9; 14 | C::printf(c"thing.y: %d\n", thing.y); 15 | if thing.y != 9 { 16 | return 2; 17 | } 18 | 19 | thing.test2.x = 11; 20 | C::printf(c"thing.test2.x: %d\n", thing.test2.x); 21 | if thing.test2.x != 11 { 22 | return 3; 23 | } 24 | 25 | thing.test2.x = thing.test2.x + 9; 26 | if thing.test2.x != 20 { 27 | return 4; 28 | } 29 | 30 | return 0; 31 | } 32 | 33 | 34 | type Test2 struct { 35 | x: int, 36 | }; 37 | 38 | type Test struct { 39 | x: int, 40 | /// Test doc comment 41 | y: int, 42 | z: f64, 43 | a: f32, 44 | e: ^u8, 45 | test2: Test2, 46 | }; 47 | -------------------------------------------------------------------------------- /tools/make-test.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # no arguments no test file 4 | if [[ $# -eq 0 ]] ; then 5 | echo 'Expected an Ark source file' 6 | echo 'usage:' 7 | echo ' ./make-test.sh tests/some-test.ark' 8 | exit 0 9 | fi 10 | 11 | # just the sourcefile with the extension 12 | source_file=$(basename "$1") 13 | 14 | # sourcefile with no extension 15 | source_filename="${source_file%.*}" 16 | 17 | # remove the extension, put a .toml on the end 18 | # note the extension is .ark which is 4 chars long 19 | # kind of hacky but it works 20 | source_no_ext=$(echo "$1"|sed 's/.\{4\}$//').toml 21 | 22 | echo "- Creating test template for '$source_filename'" 23 | echo 'Name = "'$source_filename'" 24 | Sourcefile = "'$source_file'" 25 | 26 | CompilerArgs = [] 27 | RunArgs = [] 28 | 29 | CompilerError = 0 30 | RunError = 0 31 | 32 | Input = "" 33 | 34 | CompilerOutput = "" 35 | RunOutput = ""' > $source_no_ext 36 | echo "- Complete" -------------------------------------------------------------------------------- /tests/arrays/array.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | mut arrayLitTest: []s16 = {100, 101, 102, 103, 104}; 5 | 6 | if arrayLitTest[1] != 101 { 7 | return 1; 8 | } 9 | 10 | if arrayLitTest[2] != 102 { 11 | return 2; 12 | } 13 | 14 | arrayLitTest[3] = 9999; 15 | if arrayLitTest[3] != 9999 { 16 | return 3; 17 | } 18 | 19 | mut i := 0; 20 | for i < 5 { 21 | C::printf(c"array index %d contains %d\n", i, arrayLitTest[i]); 22 | i = i + 1; 23 | } 24 | 25 | random := []int{0, 16, 32, 64, 128, 256, 512, 1024}; 26 | i = 0; 27 | for i < 8 { 28 | C::printf(c"random contains %d at index %d\n", random[i], i); 29 | i = i + 1; 30 | } 31 | 32 | mut fixedArrayLitTest := [4]int{1, 2, 3, 4}; 33 | if fixedArrayLitTest[2] != 3 { 34 | return 4; 35 | } 36 | 37 | if len(fixedArrayLitTest) != 4 { 38 | return 5; 39 | } 40 | 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /examples/pi_monte_carlo.ark: -------------------------------------------------------------------------------- 1 | [c] func printf(fmt: ^u8, ...); 2 | [c] func srand(seed: uint); 3 | [c] func time(ptr: u64) -> uint; 4 | [c] func rand() -> int; 5 | [c] func sqrt(in: f64) -> f64; 6 | 7 | func randFloat() -> f64 { 8 | RAND_MAX := 2147483647; // hack for now 9 | 10 | return f64(C::rand()) / f64(RAND_MAX); 11 | } 12 | 13 | func calculate(iters: int) -> f64 { 14 | radius: f64 = 10.0; 15 | width := radius * 2.0; 16 | 17 | mut totalInCircle: u64 = 0; 18 | 19 | mut i := 0; 20 | for i < iters { 21 | dx := randFloat() * width - radius; 22 | dy := randFloat() * width - radius; 23 | 24 | if C::sqrt(dx*dx+dy*dy) < radius { 25 | totalInCircle += 1; 26 | } 27 | 28 | i += 1; 29 | } 30 | 31 | return 4.0 * f64(totalInCircle) / f64(iters); 32 | } 33 | 34 | pub func main() -> int { 35 | C::srand(C::time(0)); 36 | 37 | iters := 20000000; 38 | res := calculate(iters); 39 | C::printf(c"Result of Monte Carlo pi estimation with %d iterations:\n", iters); 40 | C::printf(c"%f\n", res); 41 | return 0; 42 | } 43 | -------------------------------------------------------------------------------- /tests/method.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | type Thing struct { 4 | x: int 5 | }; 6 | 7 | func (v: Thing) do() { 8 | C::printf(c"did! %d\n", v.x); 9 | } 10 | 11 | func (v: ^Thing) dop() { 12 | C::printf(c"didp! %d\n", v.x); 13 | } 14 | 15 | type Thing2 int; 16 | 17 | func (v: Thing2) do() { 18 | C::printf(c"did, from an int!\n"); 19 | } 20 | 21 | func (Thing2) do() -> int { 22 | C::printf(c"did from a static method!\n"); 23 | return 0; 24 | } 25 | 26 | pub func main() -> int { 27 | mut thing: Thing; 28 | thing.x = 6; 29 | 30 | C::printf(c"%x\n", thing); 31 | 32 | thing.do(); 33 | 34 | thingptr: ^Thing = ^thing; 35 | thingptr.dop(); 36 | thingptr.do(); 37 | 38 | thingptrptr: ^^Thing = ^thingptr; 39 | thingptrptr.dop(); 40 | 41 | mut thing2: Thing2; 42 | 43 | thing2.do(); 44 | 45 | fn := Thing2::do; 46 | fn(); 47 | 48 | return Thing2::do(); 49 | } 50 | 51 | type Thing3 int; 52 | 53 | func (Thing3) do1() { 54 | Thing3::do2(); 55 | } 56 | 57 | func (Thing3) do2() { 58 | } 59 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | # which go versions do we target? 4 | go: 5 | - 1.5.2 6 | - tip 7 | 8 | notifications: 9 | email: false 10 | 11 | # container based stuffs 12 | sudo: false 13 | 14 | addons: 15 | apt: 16 | sources: 17 | - llvm-toolchain-precise-3.7 18 | - ubuntu-toolchain-r-test 19 | 20 | packages: 21 | - llvm-3.7 22 | - llvm-3.7-dev 23 | - libedit2 24 | - libedit-dev 25 | - libconfig++8-dev 26 | - g++-4.9 27 | 28 | install: 29 | # use g++-4.9 30 | - export CXX="g++-4.9" 31 | # dynamic llvm version fetching that is probably overkill 32 | - git clone https://github.com/ark-lang/go-llvm.git $GOPATH/src/github.com/ark-lang/go-llvm 33 | # install the bindings to gopath 34 | - export CGO_CPPFLAGS="`llvm-config-3.7 --cppflags`" 35 | - export CGO_LDFLAGS="`llvm-config-3.7 --ldflags --libs --system-libs all`" 36 | - export CGO_CXXFLAGS=-std=c++11 37 | - go install -tags byollvm github.com/ark-lang/go-llvm/llvm 38 | 39 | # our stuff goes here 40 | script: 41 | - go get -v -tags byollvm ./... 42 | - go install -v -tags byollvm ./... 43 | - $GOPATH/bin/ark-testrunner 44 | -------------------------------------------------------------------------------- /lib/README.md: -------------------------------------------------------------------------------- 1 | # stdlib 2 | The standard library for the Ark programming language. 3 | 4 | The standard library should be bundled with 5 | the compiler when you install, but in the 6 | mean time you can clone the stdlib and place 7 | it in any projects you are working on. 8 | 9 | Note there is no testing framework yet, but 10 | for now I'm using a simple test project to 11 | check that features work... 12 | 13 | GNU Make is required to run the test case, 14 | it will pass in the base directory (tests), 15 | pass in the entry source code file (test.ark), 16 | and it will also include `-I` the standard library 17 | folder (`std/`). 18 | 19 | ## TODO 20 | Some of these are blocked by language features 21 | being incomplete, or not implemented at all. 22 | 23 | * [ ] Memory allocation/destruction with generics 24 | * [ ] File reading/writing 25 | * [ ] Concatenation in println 26 | * [ ] Print, Print to fd, etc.. 27 | * [ ] Option types 28 | 29 | ## Contributing 30 | Great! To contribute to the standard library, 31 | please see the [contributing](/CONTRIBUTING.md) file. 32 | 33 | ## License 34 | Licensed under [MIT](/LICENSE) 35 | -------------------------------------------------------------------------------- /examples/prime.ark: -------------------------------------------------------------------------------- 1 | /** 2 | 3 | Ark: 4 | 59.66 5 | 59.14 6 | 59.09 7 | 60.12 8 | 60.00 9 | AVERAGE = 59.602 10 | 11 | C: 12 | 58.63 13 | 58.57 14 | 59.01 15 | 50.53 16 | 59.81 17 | AVERAGE = 57.31 18 | 19 | Close! We had no optimization passes either! 20 | 21 | **/ 22 | 23 | [c] func printf(fmt: ^u8, ...) -> int; 24 | [c] func sqrt(x: f64) -> f64; 25 | 26 | func is_prime(n: int) -> int { 27 | if n < 2 { 28 | return 0; 29 | } else if n == 2 { 30 | return 1; 31 | } else if n % 2 == 0 { 32 | return 0; 33 | } 34 | 35 | upper: int = int(C::sqrt(f64(n))); 36 | mut i: int = 3; 37 | 38 | for i <= upper { 39 | if n % i == 0 { 40 | return 0; 41 | } 42 | i = i + 2; 43 | } 44 | 45 | return i; 46 | } 47 | 48 | pub func main() -> int { 49 | mut no_primes: int = 0; 50 | limit: int = 67108864; 51 | mut n: int = 0; 52 | 53 | for n <= limit { 54 | if is_prime(n) >= 1 { 55 | no_primes = no_primes + 1; 56 | } 57 | n = n + 1; 58 | } 59 | 60 | C::printf(c"pi(%d) = %d\n", limit, no_primes); 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /lib/LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Ark Programming Language 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015-2016 Ark Programming Language 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /tests/fizzbuzz.toml: -------------------------------------------------------------------------------- 1 | Name = "fizzbuzz" 2 | Sourcefile = "fizzbuzz.ark" 3 | 4 | CompilerArgs = [] 5 | RunArgs = [] 6 | 7 | CompilerError = 0 8 | RunError = 0 9 | 10 | Input = "" 11 | 12 | CompilerOutput = "" 13 | RunOutput = """ 14 | 1 15 | 2 16 | Fizz 17 | 4 18 | Buzz 19 | Fizz 20 | 7 21 | 8 22 | Fizz 23 | Buzz 24 | 11 25 | Fizz 26 | 13 27 | 14 28 | FizzBuzz 29 | 16 30 | 17 31 | Fizz 32 | 19 33 | Buzz 34 | Fizz 35 | 22 36 | 23 37 | Fizz 38 | Buzz 39 | 26 40 | Fizz 41 | 28 42 | 29 43 | FizzBuzz 44 | 31 45 | 32 46 | Fizz 47 | 34 48 | Buzz 49 | Fizz 50 | 37 51 | 38 52 | Fizz 53 | Buzz 54 | 41 55 | Fizz 56 | 43 57 | 44 58 | FizzBuzz 59 | 46 60 | 47 61 | Fizz 62 | 49 63 | Buzz 64 | Fizz 65 | 52 66 | 53 67 | Fizz 68 | Buzz 69 | 56 70 | Fizz 71 | 58 72 | 59 73 | FizzBuzz 74 | 61 75 | 62 76 | Fizz 77 | 64 78 | Buzz 79 | Fizz 80 | 67 81 | 68 82 | Fizz 83 | Buzz 84 | 71 85 | Fizz 86 | 73 87 | 74 88 | FizzBuzz 89 | 76 90 | 77 91 | Fizz 92 | 79 93 | Buzz 94 | Fizz 95 | 82 96 | 83 97 | Fizz 98 | Buzz 99 | 86 100 | Fizz 101 | 88 102 | 89 103 | FizzBuzz 104 | 91 105 | 92 106 | Fizz 107 | 94 108 | Buzz 109 | Fizz 110 | 97 111 | 98 112 | Fizz 113 | Buzz 114 | """ 115 | -------------------------------------------------------------------------------- /tests/prime_sieve.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | pub func main() -> int { 4 | // oh god the horror 5 | mut primes: []bool = []bool{ 6 | false, true, true, true, true, true, true, true, true, true, 7 | true, true, true, true, true, true, true, true, true, true, 8 | true, true, true, true, true, true, true, true, true, true, 9 | true, true, true, true, true, true, true, true, true, true, 10 | true, true, true, true, true, true, true, true, true, true, 11 | true, true, true, true, true, true, true, true, true, true, 12 | true, true, true, true, true, true, true, true, true, true, 13 | true, true, true, true, true, true, true, true, true, true, 14 | true, true, true, true, true, true, true, true, true, true, 15 | true, true, true, true, true, true, true, true, true, true, 16 | true, true, true, true, true, true, true, true, true, true, 17 | }; 18 | 19 | mut numPrimes: int = 0; 20 | mut i := 2; 21 | for i < 100 { 22 | if primes[i-1] { 23 | C::printf(c"`%d` is prime\n", i); 24 | 25 | numPrimes = numPrimes + 1; 26 | 27 | mut j := i+1; 28 | for j < 100 { 29 | if j % i == 0 { 30 | primes[j-1] = false; 31 | } 32 | j = j + 1; 33 | } 34 | } 35 | i = i + 1; 36 | } 37 | 38 | return 25 - numPrimes; 39 | } 40 | -------------------------------------------------------------------------------- /tests/if.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | mut globInt := 0; 4 | 5 | func do() -> bool { 6 | globInt += 1; 7 | return false; 8 | } 9 | 10 | mut globInt2 := 0; 11 | 12 | func do2() -> bool { 13 | globInt2 += 1; 14 | return true; 15 | } 16 | 17 | mut globInt3 := 0; 18 | 19 | func do3() -> bool { 20 | globInt3 += 1; 21 | return false; 22 | } 23 | 24 | // TODO create a separate logical binop test 25 | pub func main() -> int { 26 | mut x: int = 5; 27 | if x == 5 { 28 | C::printf(c"this is a test\n"); 29 | } 30 | C::printf(c"always printed\n"); 31 | 32 | if 5 == 6 { 33 | return 1; 34 | } 35 | 36 | if do() && false && do() && do() { 37 | 38 | } 39 | 40 | C::printf(c"%d\n", globInt); 41 | if globInt != 1 { 42 | return 1; 43 | } 44 | 45 | if do2() || do2() { 46 | 47 | } 48 | 49 | if globInt2 != 1 { 50 | return 2; 51 | } 52 | 53 | if do3() || do3() { 54 | 55 | } 56 | 57 | if globInt3 != 2 { 58 | return 3; 59 | } 60 | 61 | if 1 == 1 { 62 | C::printf(c"if.ark test is a success!\n"); 63 | return 0; 64 | } else { 65 | if 1 == 2 { 66 | if 1 > 4 { 67 | return 9; 68 | } else if x < 10 { 69 | return 123; 70 | } else { 71 | return 9; 72 | } 73 | } else if x > 10 { 74 | return 10; 75 | } else { 76 | return 11; 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /src/parser/misc.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "errors" 5 | "strings" 6 | ) 7 | 8 | func hexRuneToInt(r rune) int { 9 | if r >= '0' && r <= '9' { 10 | return int(r - '0') 11 | } else if r >= 'A' && r <= 'F' { 12 | return int(r-'A') + 10 13 | } else if r >= 'a' && r <= 'f' { 14 | return int(r-'a') + 10 15 | } else { 16 | return -1 17 | } 18 | } 19 | 20 | func octRuneToInt(r rune) int { 21 | if r >= '0' && r <= '7' { 22 | return int(r - '0') 23 | } else { 24 | return -1 25 | } 26 | } 27 | 28 | func binRuneToInt(r rune) int { 29 | if r == '0' { 30 | return 0 31 | } else if r == '1' { 32 | return 1 33 | } else { 34 | return -1 35 | } 36 | } 37 | 38 | const ( 39 | SIMPLE_ESCAPE_VALUES string = "\a\b\f\n\r\t\v\\'\"" + string(0) 40 | SIMPLE_ESCAPE_NAMES string = "abfnrtv\\'\"0" 41 | ) 42 | 43 | func UnescapeString(s string) (string, error) { 44 | out := make([]rune, 0) 45 | sr := []rune(s) 46 | 47 | for i := 0; i < len(sr); i++ { 48 | if sr[i] == '\\' { 49 | i++ 50 | 51 | index := strings.IndexRune(SIMPLE_ESCAPE_NAMES, sr[i]) 52 | 53 | if index < 0 { 54 | return "", errors.New("bad escape: `\\" + string(sr[i]) + "`") 55 | } 56 | 57 | out = append(out, []rune(SIMPLE_ESCAPE_VALUES)[index]) 58 | } else { 59 | out = append(out, sr[i]) 60 | } 61 | } 62 | 63 | return string(out), nil 64 | } 65 | -------------------------------------------------------------------------------- /src/ast/misc.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/parser" 5 | "github.com/ark-lang/ark/src/util" 6 | ) 7 | 8 | // escape for debug output 9 | // only things that can't be displayed need to be escaped 10 | func EscapeString(s string) string { 11 | out := make([]rune, 0) 12 | sr := []rune(s) 13 | 14 | main_loop: 15 | for _, r := range sr { 16 | for i, escapeVal := range []rune(parser.SIMPLE_ESCAPE_VALUES) { 17 | if r == escapeVal { 18 | out = append(out, '\\', []rune(parser.SIMPLE_ESCAPE_NAMES)[i]) 19 | continue main_loop 20 | } 21 | } 22 | out = append(out, r) 23 | } 24 | 25 | return string(out) 26 | } 27 | 28 | func colorizeEscapedString(input string) string { 29 | inputRunes := []rune(input) 30 | outputRunes := make([]rune, 31 | len(inputRunes)) 32 | 33 | outputRunes = append(outputRunes, []rune(util.TEXT_YELLOW)...) 34 | for i := 0; i < len(inputRunes); i++ { 35 | if inputRunes[i] == '\\' { 36 | outputRunes = append(outputRunes, []rune(util.TEXT_RESET+util.TEXT_CYAN)...) 37 | i++ 38 | outputRunes = append(outputRunes, '\\', inputRunes[i]) 39 | outputRunes = append(outputRunes, []rune(util.TEXT_YELLOW)...) 40 | continue 41 | } 42 | outputRunes = append(outputRunes, inputRunes[i]) 43 | } 44 | 45 | outputRunes = append(outputRunes, []rune(util.TEXT_RESET)...) 46 | return string(outputRunes) 47 | } 48 | -------------------------------------------------------------------------------- /lib/tests/test-utf8.ark: -------------------------------------------------------------------------------- 1 | #use std::unicode::utf8 2 | #use std::mem 3 | 4 | [c] func printf(fmt: ^u8, ...) -> int; 5 | 6 | tests := []string{ 7 | "Iñtërnâtiônàlizætiøn", 8 | "κόσμε", 9 | "東京", 10 | "北京市", 11 | }; 12 | 13 | pub func main() -> int { 14 | mut i: uint = 0; 15 | for i < len(tests) { 16 | test := tests[i]; 17 | C::printf(c"test: %d, len(test): %d, ", i, len(test)); 18 | 19 | runes := utf8::numRunes(test); 20 | C::printf(c"#runes: %d, ", runes); 21 | 22 | dest := mem::allocArray(runes); 23 | utf8::decode(dest, test); 24 | 25 | bytes := utf8::numBytes(dest); 26 | C::printf(c"#bytes: %d\n", bytes); 27 | 28 | mut h: uint = 0; 29 | C::printf(c"runes: "); 30 | for h < len(dest) { 31 | C::printf(c"%d ", dest[h]); 32 | h += 1; 33 | } 34 | C::printf(c"\n"); 35 | 36 | testCopy := mem::allocArray(bytes); 37 | utf8::encode(testCopy, dest); 38 | 39 | // Verify that the encoded output matches the original test 40 | if len(test) != len(testCopy) { 41 | C::printf(c"len(test) != len(testCopy) : %d != %d\n", len(test), len(testCopy)); 42 | return 1; 43 | } 44 | 45 | mut j: uint = 0; 46 | for j < len(test) { 47 | x := test[j]; 48 | y := testCopy[j]; 49 | if x != y { 50 | C::printf(c"#%d: %d != %d\n", j, x, y); 51 | return 1; 52 | } 53 | j += 1; 54 | } 55 | 56 | i += 1; 57 | } 58 | return 0; 59 | } -------------------------------------------------------------------------------- /tests/generic.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | type Option enum { 4 | None, 5 | Some(T, u8) 6 | }; 7 | 8 | type RC struct { 9 | refs: int, 10 | val: ^T, 11 | }; 12 | 13 | func do(t: T) -> T { 14 | return t; 15 | } 16 | 17 | #use std::mem 18 | 19 | pub func main() -> int { 20 | 21 | a: int = 0; 22 | x := Option::None; 23 | y := Option::Some(a, 5); 24 | 25 | C::printf(c"x tag: %d\n", x); 26 | C::printf(c"y tag: %d\n", y); 27 | 28 | h: u8 = 1; 29 | mut i := RC{refs: 0}; 30 | i.val = ^h; 31 | 32 | mut ival := i.val; 33 | ival = ^h; 34 | 35 | C::printf(c"sizeof(@i.val) = %d\n", sizeof(@i.val)); 36 | if sizeof(@i.val) != 1 { 37 | return 1; 38 | } 39 | 40 | j: u16 = 1; 41 | mut k := RC{refs: 0}; 42 | k.val = ^j; 43 | C::printf(c"sizeof(@k.val) = %d\n", sizeof(@k.val)); 44 | if sizeof(@k.val) != 2 { 45 | return 1; 46 | } 47 | 48 | l := do(5); 49 | C::printf(c"l = %d\n", l); 50 | m := do(10); 51 | C::printf(c"m = %d\n", m); 52 | str := do<^u8>(c"hi!"); 53 | C::printf(c"str = \"%s\"\n", str); 54 | 55 | n := mem::alloc(); 56 | o := n; 57 | @o = 111; 58 | C::printf(c"@o = %d\n", @o); 59 | mem::free(n); 60 | 61 | mut arr := mem::allocArray(5); 62 | arr[4] = 4; 63 | mem::freeArray(arr); 64 | 65 | return 0; 66 | } 67 | -------------------------------------------------------------------------------- /src/semantic/misc.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/ast" 5 | "github.com/ark-lang/ark/src/util" 6 | ) 7 | 8 | type MiscCheck struct { 9 | InFunction int 10 | } 11 | 12 | func (_ MiscCheck) Name() string { return "misc" } 13 | 14 | func (v *MiscCheck) Init(s *SemanticAnalyzer) { 15 | v.InFunction = 0 16 | } 17 | 18 | func (v *MiscCheck) EnterScope(s *SemanticAnalyzer) {} 19 | func (v *MiscCheck) ExitScope(s *SemanticAnalyzer) {} 20 | 21 | func (v *MiscCheck) Visit(s *SemanticAnalyzer, n ast.Node) { 22 | switch n.(type) { 23 | case *ast.FunctionDecl, *ast.LambdaExpr: 24 | v.InFunction++ 25 | } 26 | 27 | if v.InFunction <= 0 { 28 | switch n.(type) { 29 | case *ast.ReturnStat: 30 | s.Err(n, "%s must be in function", util.CapitalizeFirst(n.NodeName())) 31 | } 32 | } else { 33 | switch n.(type) { 34 | case *ast.TypeDecl: 35 | s.Err(n, "%s must not be in function", util.CapitalizeFirst(n.NodeName())) 36 | 37 | case *ast.FunctionDecl: 38 | if v.InFunction > 1 { 39 | s.Err(n, "%s must not be in function", util.CapitalizeFirst(n.NodeName())) 40 | } 41 | } 42 | } 43 | } 44 | 45 | func (v *MiscCheck) PostVisit(s *SemanticAnalyzer, n ast.Node) { 46 | switch n.(type) { 47 | case *ast.FunctionDecl, *ast.LambdaExpr: 48 | v.InFunction-- 49 | } 50 | } 51 | 52 | func (v *MiscCheck) Finalize(s *SemanticAnalyzer) { 53 | 54 | } 55 | -------------------------------------------------------------------------------- /src/semantic/immutable_assign.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/ast" 5 | ) 6 | 7 | type ImmutableAssignCheck struct { 8 | } 9 | 10 | func (_ ImmutableAssignCheck) Name() string { return "immutable assign" } 11 | 12 | func (v *ImmutableAssignCheck) Init(s *SemanticAnalyzer) {} 13 | func (v *ImmutableAssignCheck) EnterScope(s *SemanticAnalyzer) {} 14 | func (v *ImmutableAssignCheck) ExitScope(s *SemanticAnalyzer) {} 15 | 16 | func (v *ImmutableAssignCheck) PostVisit(s *SemanticAnalyzer, n ast.Node) {} 17 | 18 | func (v *ImmutableAssignCheck) Visit(s *SemanticAnalyzer, n ast.Node) { 19 | switch n := n.(type) { 20 | case *ast.AssignStat: 21 | if !n.Access.Mutable() { 22 | s.Err(n, "Cannot assign value to immutable access") 23 | } 24 | 25 | case *ast.BinopAssignStat: 26 | if !n.Access.Mutable() { 27 | s.Err(n, "Cannot assign value to immutable access") 28 | } 29 | 30 | case *ast.DestructAssignStat: 31 | for _, acc := range n.Accesses { 32 | if !acc.Mutable() { 33 | s.Err(acc, "Cannot assign value to immutable access") 34 | } 35 | } 36 | 37 | case *ast.DestructBinopAssignStat: 38 | for _, acc := range n.Accesses { 39 | if !acc.Mutable() { 40 | s.Err(acc, "Cannot assign value to immutable access") 41 | } 42 | } 43 | } 44 | } 45 | 46 | func (v *ImmutableAssignCheck) Finalize(s *SemanticAnalyzer) { 47 | 48 | } 49 | -------------------------------------------------------------------------------- /src/lexer/token.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | type TokenType int 4 | 5 | const ( 6 | Rune TokenType = iota 7 | Identifier 8 | Separator 9 | Operator 10 | Number 11 | Erroneous 12 | String 13 | Doccomment 14 | ) 15 | 16 | var tokenStrings = []string{"rune", "identifier", "separator", "operator", "number", "erroneous", "string", "doccomment"} 17 | 18 | func (v TokenType) String() string { 19 | return tokenStrings[v] 20 | } 21 | 22 | type Token struct { 23 | Type TokenType 24 | Contents string 25 | Where Span 26 | } 27 | 28 | type Position struct { 29 | Filename string 30 | 31 | Line, Char int 32 | } 33 | 34 | type Span struct { 35 | Filename string 36 | 37 | StartLine, StartChar int 38 | EndLine, EndChar int 39 | } 40 | 41 | func NewSpan(start, end Position) Span { 42 | return Span{Filename: start.Filename, 43 | StartLine: start.Line, StartChar: start.Char, 44 | EndLine: end.Line, EndChar: end.Char, 45 | } 46 | } 47 | 48 | func NewSpanFromTokens(start, end *Token) Span { 49 | return Span{Filename: start.Where.Filename, 50 | StartLine: start.Where.StartLine, StartChar: start.Where.StartChar, 51 | EndLine: end.Where.EndLine, EndChar: end.Where.EndChar, 52 | } 53 | } 54 | 55 | func (s Span) Start() Position { 56 | return Position{Filename: s.Filename, 57 | Line: s.StartLine, Char: s.StartChar} 58 | } 59 | 60 | func (s Span) End() Position { 61 | return Position{Filename: s.Filename, 62 | Line: s.EndLine, Char: s.EndChar} 63 | } 64 | -------------------------------------------------------------------------------- /src/util/color.go: -------------------------------------------------------------------------------- 1 | package util 2 | 3 | import ( 4 | "os" 5 | "runtime" 6 | ) 7 | 8 | var ( 9 | TEXT_RESET string = "" 10 | TEXT_BOLD string = "" 11 | TEXT_RED string = "" 12 | TEXT_GREEN string = "" 13 | TEXT_YELLOW string = "" 14 | TEXT_BLUE string = "" 15 | TEXT_MAGENTA string = "" 16 | TEXT_CYAN string = "" 17 | TEXT_WHITE string = "" 18 | ) 19 | 20 | func init() { 21 | if os.Getenv("COLOR") == "0" { 22 | return 23 | } 24 | 25 | switch runtime.GOOS { 26 | case "linux", "darwin", "freebsd": 27 | TEXT_RESET = "\x1B[00m" 28 | TEXT_BOLD = "\x1B[01m" 29 | TEXT_RED = "\x1B[31m" 30 | TEXT_GREEN = "\x1B[32m" 31 | TEXT_YELLOW = "\x1B[33m" 32 | TEXT_BLUE = "\x1B[34m" 33 | TEXT_MAGENTA = "\x1B[35m" 34 | TEXT_CYAN = "\x1B[36m" 35 | TEXT_WHITE = "\x1B[37m" 36 | } 37 | } 38 | 39 | func Bold(s string) string { 40 | return TEXT_BOLD + s + TEXT_RESET 41 | } 42 | 43 | func Red(s string) string { 44 | return TEXT_RED + s + TEXT_RESET 45 | } 46 | 47 | func Green(s string) string { 48 | return TEXT_GREEN + s + TEXT_RESET 49 | } 50 | 51 | func Yellow(s string) string { 52 | return TEXT_YELLOW + s + TEXT_RESET 53 | } 54 | 55 | func Blue(s string) string { 56 | return TEXT_BLUE + s + TEXT_RESET 57 | } 58 | 59 | func Magenta(s string) string { 60 | return TEXT_MAGENTA + s + TEXT_RESET 61 | } 62 | 63 | func Cyan(s string) string { 64 | return TEXT_CYAN + s + TEXT_RESET 65 | } 66 | 67 | func White(s string) string { 68 | return TEXT_WHITE + s + TEXT_RESET 69 | } 70 | -------------------------------------------------------------------------------- /src/doc/index.go: -------------------------------------------------------------------------------- 1 | package doc 2 | 3 | import ( 4 | "html/template" 5 | "os" 6 | ) 7 | 8 | var indexTemplate = template.Must(template.New("index").Parse(INDEX_TEMPLATE_STR)) 9 | 10 | type IndexTempData struct { 11 | Files []*File 12 | } 13 | 14 | func (v *Docgen) generateIndex() { 15 | out, err := os.OpenFile(v.Dir+"index.html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 16 | if err != nil { 17 | panic(err) 18 | } 19 | defer out.Close() 20 | 21 | indexTempData := IndexTempData{ 22 | Files: v.output, 23 | } 24 | 25 | err = indexTemplate.Execute(out, indexTempData) 26 | if err != nil { 27 | panic(err) 28 | } 29 | } 30 | 31 | const INDEX_TEMPLATE_STR = ` 32 | 33 | 34 | 35 | Docs Index 36 | 37 | 38 | 39 | 40 | 41 | 42 |
43 |
44 |

Docgen Index

45 | Index 46 |
47 |
48 | 49 |
50 | 53 |
54 | 55 | ` 56 | -------------------------------------------------------------------------------- /tests/stdlib/utf8.ark: -------------------------------------------------------------------------------- 1 | #use std::io 2 | #use std::mem 3 | #use std::unicode::utf8 4 | 5 | TEST_STRINGS := []string{ 6 | "hello", 7 | "Test string 1234\n", 8 | "こんにちは", 9 | "東京", 10 | "你好", 11 | "weiß", 12 | "ЁЂЃЄЅІЇЈЉЊЋЌЍЎЏАБВГДЕЖЗИЙКЛМНОПРСТУФХЦЧШЩЪЫЬЭЮЯабвгдежзийклмнопрстуфхцчшщъыьэюя", 13 | "❤️ 💔 💌 💕 💞 💓 💗 💖 💘 💝 💟 💜 💛 💚 💙", 14 | "1;DROP TABLE users", 15 | "울란바토르", 16 | "゚・✿ヾ╲(。◕‿◕。)╱✿・゚", 17 | }; 18 | 19 | TEST_STRING_NUM_RUNES := []uint{ 20 | 5, 21 | 17, 22 | 5, 23 | 2, 24 | 2, 25 | 4, 26 | 79, 27 | 30, 28 | 18, 29 | 5, 30 | 16, 31 | }; 32 | 33 | pub func main() -> int { 34 | mut i: uint = 0; 35 | for i < len(TEST_STRINGS) { 36 | 37 | numRunes := utf8::numRunes([]u8(TEST_STRINGS[i])); 38 | if numRunes != TEST_STRING_NUM_RUNES[i] { 39 | return 1; 40 | } 41 | 42 | runeArray := mem::allocArray(numRunes); 43 | utf8::decode(runeArray, []u8(TEST_STRINGS[i])); 44 | numBytes := utf8::numBytes(runeArray); 45 | if numBytes != len(TEST_STRINGS[i]) { 46 | return 2; 47 | } 48 | 49 | byteArray := mem::allocArray(numBytes); 50 | utf8::encode(byteArray, runeArray); 51 | mut t: uint = 0; 52 | for t < numBytes { 53 | if byteArray[t] != TEST_STRINGS[i][t] { 54 | return 3; 55 | } 56 | t += 1; 57 | } 58 | 59 | i += 1; 60 | } 61 | 62 | return 0; 63 | } 64 | -------------------------------------------------------------------------------- /tests/named_types.ark: -------------------------------------------------------------------------------- 1 | #[c] func printf(fmt: ^u8, ...) -> int; 2 | 3 | type thing int; 4 | type thing2 thing; 5 | 6 | type tupleType (int, int); 7 | 8 | type arrayType [4]u8; 9 | 10 | type structType struct { 11 | x: int 12 | }; 13 | 14 | pub func main() -> int { 15 | C::printf(c"Named types test.\n"); 16 | 17 | // primitive test 18 | fnVal: int = 7; 19 | fn(thing(fnVal)); 20 | fn(8); 21 | 22 | t: thing2 = 5; 23 | fn(thing(t)); 24 | 25 | fn2(-6); 26 | 27 | // tuple test 28 | tupleVal: tupleType = (5, 6); 29 | tupleTest(tupleVal); 30 | 31 | tupleVal2: (int, int) = (7, 8); 32 | tupleTest(tupleType(tupleVal2)); 33 | 34 | // array test 35 | mut arrayVal: arrayType = arrayType{1, 2, 3, 4}; 36 | 37 | arrayVal[0] = 5; 38 | 39 | arrayTest(arrayVal); 40 | 41 | // struct test 42 | mut structVal: structType; 43 | structVal.x = 6; 44 | 45 | mut structAnon: struct { 46 | z: int 47 | }; 48 | 49 | structAnon.z = 100; 50 | structAnonTest(structAnon); 51 | 52 | return 0; 53 | } 54 | 55 | func fn(y: thing) { 56 | C::printf(c"y: %d\n", y); 57 | } 58 | 59 | func fn2(y: thing2) { 60 | C::printf(c"y: %d\n", y); 61 | } 62 | 63 | func arrayTest(y: arrayType) { 64 | C::printf(c"[%d, %d, %d, %d]\n", y[0], y[1], y[2], y[3]); 65 | } 66 | 67 | func tupleTest(y: tupleType) { 68 | // (a, b) := y; 69 | //C::printf(c"(%d, %d)\n", a, b); 70 | } 71 | 72 | func structAnonTest(struct_: struct {z: int}) { 73 | C::printf(c"%d\n", struct_.z); 74 | } 75 | -------------------------------------------------------------------------------- /src/cmd/ark/args.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "gopkg.in/alecthomas/kingpin.v2" 5 | ) 6 | 7 | var ( 8 | app = kingpin.New("ark", "Compiler for the Ark programming language.").Version(VERSION).Author(AUTHOR) 9 | 10 | logLevel = app.Flag("loglevel", "Set the level of logging to show").Default("info").Enum("debug", "verbose", "info", "warning", "error") 11 | logTags = app.Flag("logtags", "Which log tags to show").Default("all").String() 12 | 13 | buildCom = app.Command("build", "Build an executable.") 14 | buildOutput = buildCom.Flag("output", "Output binary name.").Short('o').Default("main").String() 15 | buildSearchpaths = buildCom.Flag("searchpaths", "Paths to search for used modules if not found in base directory").Short('I').Strings() 16 | buildInput = buildCom.Arg("input", "Ark source file or package").String() 17 | buildCodegen = buildCom.Flag("codegen", "Codegen backend to use").Default("llvm").Enum("none", "llvm") 18 | buildOutputType = buildCom.Flag("output-type", "The format to produce after code generation").Default("executable").Enum("executable", "assembly", "object", "llvm-ir") 19 | buildOptLevel = buildCom.Flag("opt-level", "LLVM optimization level").Short('O').Default("0").Int() 20 | ignoreUnused = buildCom.Flag("unused", "Do not error on unused declarations").Bool() 21 | 22 | docgenCom = app.Command("docgen", "Generate documentation.") 23 | docgenDir = docgenCom.Flag("dir", "Directory to place generated docs in.").Default("docgen").String() 24 | docgenInput = docgenCom.Arg("input", "Ark source file or package").String() 25 | docgenSearchpaths = docgenCom.Flag("searchpaths", "Paths to search for used modules if not found in base directory").Short('I').Strings() 26 | ) 27 | -------------------------------------------------------------------------------- /src/parser/attr.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/lexer" 5 | "github.com/ark-lang/ark/src/util" 6 | ) 7 | 8 | type AttrGroup map[string]*Attr 9 | 10 | func (v AttrGroup) Contains(key string) bool { 11 | _, ok := v[key] 12 | return ok 13 | } 14 | 15 | func (v AttrGroup) Get(key string) *Attr { 16 | return v[key] 17 | } 18 | 19 | func (v AttrGroup) Set(key string, value *Attr) bool { 20 | _, ok := v[key] 21 | v[key] = value 22 | return ok 23 | } 24 | 25 | func (v AttrGroup) Extend(other AttrGroup) { 26 | for _, attr := range other { 27 | v[attr.Key] = attr 28 | } 29 | } 30 | 31 | func (v AttrGroup) Equals(other AttrGroup) bool { 32 | if len(v) != len(other) { 33 | return false 34 | } 35 | 36 | for key, value := range v { 37 | otherValue, ok := other[key] 38 | if !ok { 39 | return false 40 | } 41 | 42 | if value.Value != otherValue.Value { 43 | return false 44 | } 45 | } 46 | 47 | return true 48 | } 49 | 50 | func (v AttrGroup) String() string { 51 | var strs []string 52 | for _, attr := range v { 53 | strs = append(strs, attr.String()) 54 | } 55 | 56 | var res string 57 | for i, str := range strs { 58 | res += str 59 | if i < len(strs)-1 { 60 | res += " " 61 | } 62 | } 63 | return res 64 | } 65 | 66 | type Attr struct { 67 | Key string 68 | Value string 69 | FromBlock bool 70 | pos lexer.Position 71 | } 72 | 73 | func (v *Attr) String() string { 74 | result := "[" + v.Key 75 | if v.Value == "" { 76 | result += "]" 77 | } else { 78 | result += "=\"" + v.Value + "\"]" 79 | } 80 | return util.Green(result) 81 | } 82 | 83 | func (v *Attr) Pos() lexer.Position { 84 | return v.pos 85 | } 86 | 87 | func (v *Attr) SetPos(pos lexer.Position) { 88 | v.pos = pos 89 | } 90 | -------------------------------------------------------------------------------- /src/semantic/deprecated.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "fmt" 5 | 6 | "github.com/ark-lang/ark/src/ast" 7 | ) 8 | 9 | type DeprecatedCheck struct { 10 | } 11 | 12 | func (_ DeprecatedCheck) Name() string { return "deprecated" } 13 | 14 | func (v *DeprecatedCheck) WarnDeprecated(s *SemanticAnalyzer, thing ast.Locatable, typ, name, message string) { 15 | mess := fmt.Sprintf("Access of deprecated %s `%s`", typ, name) 16 | if message == "" { 17 | s.Warn(thing, mess) 18 | } else { 19 | s.Warn(thing, mess+": "+message) 20 | } 21 | } 22 | 23 | func (v *DeprecatedCheck) checkTypeReference(s *SemanticAnalyzer, loc ast.Locatable, typref *ast.TypeReference) { 24 | if dep := typref.BaseType.Attrs().Get("deprecated"); dep != nil { 25 | v.WarnDeprecated(s, loc, "type", typref.BaseType.TypeName(), dep.Value) 26 | } 27 | 28 | for _, garg := range typref.GenericArguments { 29 | v.checkTypeReference(s, loc, garg) 30 | } 31 | } 32 | 33 | func (v *DeprecatedCheck) Init(s *SemanticAnalyzer) {} 34 | func (v *DeprecatedCheck) EnterScope(s *SemanticAnalyzer) {} 35 | func (v *DeprecatedCheck) ExitScope(s *SemanticAnalyzer) {} 36 | 37 | func (v *DeprecatedCheck) PostVisit(s *SemanticAnalyzer, n ast.Node) {} 38 | 39 | func (v *DeprecatedCheck) Visit(s *SemanticAnalyzer, n ast.Node) { 40 | switch n := n.(type) { 41 | case *ast.VariableDecl: 42 | v.checkTypeReference(s, n, n.Variable.Type) 43 | 44 | case *ast.CallExpr: 45 | /*if dep := n.Function.Type.Attrs().Get("deprecated"); dep != nil { 46 | v.WarnDeprecated(s, n, "function", n.Function.Name, dep.Value) 47 | }*/ // TODO 48 | 49 | case *ast.VariableAccessExpr: 50 | if dep := n.Variable.Attrs.Get("deprecated"); dep != nil { 51 | v.WarnDeprecated(s, n, "variable", n.Variable.Name, dep.Value) 52 | } 53 | } 54 | } 55 | 56 | func (v *DeprecatedCheck) Finalize(s *SemanticAnalyzer) { 57 | 58 | } 59 | -------------------------------------------------------------------------------- /lib/design.md: -------------------------------------------------------------------------------- 1 | # Standard Library 2 | The standard library for Ark should contain the bare 3 | minimum for a programmer to accomplish their daily 4 | tasks. The library should adopt a consistent style 5 | guide and paradigm for the implementation. 6 | 7 | At least, the library should contain libraries for: 8 | * Input and output (buffered) 9 | * Reader 10 | * Writer 11 | * File IO wrappers on top of the basic reader/writer? 12 | * Basic data structures 13 | * HashMap 14 | * List [x] 15 | * Linked List 16 | * Stack [x] 17 | * Ordered binary tree? 18 | * Formatting for I/O 19 | * Math 20 | * Big math 21 | * Psuedo-random generators 22 | * Complex (128 bit stuff) 23 | * OS 24 | * Processes 25 | * Signals 26 | * System calls? 27 | * Path 28 | * Regex 29 | * Sorting 30 | * Compression 31 | * zlib 32 | * gzip 33 | * lzw 34 | * DEFLATE 35 | * Concurrency 36 | * Threads 37 | * Mutexes 38 | * Channels 39 | * Date/time 40 | * Strings 41 | * UTF-8 [-] 42 | * Iterators 43 | 44 | Probably an official external library" 45 | * Cryptography 46 | * AES 128/256 47 | * SHA 1/256/512 48 | * MD4 49 | * RSA 50 | * Blowfish & Twofish? 51 | 52 | ### Notes 53 | Cryptography will likely be done via a C library wrapper as an external library. 54 | 55 | I'm open to adding more data structures, but right now they seem like the most 56 | common data structures that we should implement. 57 | 58 | For the HashMap I'm thinking we could has SipHash, though it is known to be 59 | quite slow it's cryptographically secure. We could probably implement an 60 | alternative HashMap that is faster and is used for cases where you don't 61 | care about collisions, etc. 62 | 63 | The Linked List would be a doubly linked list, which means it can be used 64 | as a queue too and is quite fast. 65 | 66 | The formatting library for IO would eventually take advantage of the macro 67 | system that doesn't yet exist. 68 | 69 | With regards to paths, I'm not sure if this should go under OS, or IO or maybe 70 | an IO utility module? 71 | -------------------------------------------------------------------------------- /src/semantic/use_before_declare.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/ast" 5 | ) 6 | 7 | type UseBeforeDeclareCheck struct { 8 | scopes []map[string]bool 9 | scope map[string]bool 10 | } 11 | 12 | func (_ UseBeforeDeclareCheck) Name() string { return "use before declare" } 13 | 14 | func (v *UseBeforeDeclareCheck) Init(s *SemanticAnalyzer) { 15 | v.scopes = nil 16 | 17 | v.scope = make(map[string]bool) 18 | for name, ident := range s.Module.ModScope.Idents { 19 | if ident.Type == ast.IDENT_VARIABLE { 20 | v.scope[name] = true 21 | } 22 | } 23 | } 24 | 25 | func (v *UseBeforeDeclareCheck) EnterScope(s *SemanticAnalyzer) { 26 | lastScope := v.scope 27 | if v.scope != nil { 28 | v.scopes = append(v.scopes, v.scope) 29 | } 30 | 31 | v.scope = make(map[string]bool) 32 | for name, declared := range lastScope { 33 | v.scope[name] = declared 34 | } 35 | } 36 | func (v *UseBeforeDeclareCheck) ExitScope(s *SemanticAnalyzer) { 37 | v.scope = nil 38 | if len(v.scopes) > 0 { 39 | idx := len(v.scopes) - 1 40 | v.scope, v.scopes[idx] = v.scopes[idx], nil 41 | v.scopes = v.scopes[:idx] 42 | } 43 | } 44 | 45 | func (v *UseBeforeDeclareCheck) PostVisit(s *SemanticAnalyzer, n ast.Node) {} 46 | 47 | func (v *UseBeforeDeclareCheck) Visit(s *SemanticAnalyzer, n ast.Node) { 48 | switch n := n.(type) { 49 | case *ast.VariableDecl: 50 | v.scope[n.Variable.Name] = true 51 | 52 | case *ast.DestructVarDecl: 53 | for idx, vari := range n.Variables { 54 | if !n.ShouldDiscard[idx] { 55 | v.scope[vari.Name] = true 56 | } 57 | } 58 | 59 | case *ast.EnumPatternExpr: 60 | for _, vari := range n.Variables { 61 | if vari != nil { 62 | v.scope[vari.Name] = true 63 | } 64 | } 65 | 66 | case *ast.VariableAccessExpr: 67 | if !v.scope[n.Variable.Name] && n.Variable.ParentModule == s.Submodule.Parent { 68 | s.Err(n, "Use of variable before declaration: %s", n.Variable.Name) 69 | } 70 | } 71 | } 72 | 73 | func (v *UseBeforeDeclareCheck) Finalize(s *SemanticAnalyzer) { 74 | 75 | } 76 | -------------------------------------------------------------------------------- /src/doc/style.go: -------------------------------------------------------------------------------- 1 | package doc 2 | 3 | import ( 4 | "os" 5 | ) 6 | 7 | func (v *Docgen) generateStyle() { 8 | out, err := os.OpenFile(v.Dir+"style.css", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 9 | if err != nil { 10 | panic(err) 11 | } 12 | defer out.Close() 13 | 14 | out.WriteString(STYLE) 15 | } 16 | 17 | const STYLE = ` 18 | * { 19 | margin: 0; 20 | padding: 0; 21 | box-sizing: border-box; 22 | } 23 | 24 | html { 25 | overflow-y: scroll; 26 | } 27 | 28 | body { 29 | font-family: "Source Serif Pro", Georgia, Times New Roman, serif; 30 | } 31 | 32 | body > .wrapper { 33 | background: #eee; 34 | margin-top: 20px; 35 | } 36 | 37 | .wrapper { 38 | display: block; 39 | width: 980px; 40 | margin: 0 auto; 41 | padding: 20px; 42 | } 43 | 44 | a { 45 | font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 46 | } 47 | 48 | .slab a { 49 | color: #fff; 50 | font-weight: 800; 51 | } 52 | 53 | .slab a:hover { 54 | color: #eee; 55 | } 56 | 57 | a { 58 | color: #24424F; 59 | text-decoration: none; 60 | } 61 | 62 | a:hover { 63 | color: #112833; 64 | text-decoration: underline; 65 | } 66 | 67 | section.doc { 68 | padding: 20px; 69 | } 70 | 71 | .slab { 72 | display: block; 73 | width: 100%; 74 | background: #546E7A; 75 | padding: 2rem; 76 | color: #fff; 77 | } 78 | 79 | .slab-title { 80 | font-size: 3em; 81 | color: #fff; 82 | } 83 | 84 | ul { 85 | margin-left: 20px; 86 | } 87 | 88 | h1, h2, h3, h4, h6 { 89 | font-family: "Fira Sans", "Helvetica Neue", Helvetica, Arial, sans-serif; 90 | } 91 | 92 | pre, code { 93 | font-family: "Fira Mono", monospace; 94 | } 95 | 96 | a { 97 | text-decoration: none; 98 | } 99 | 100 | .declname { 101 | margin-top: 1em; 102 | } 103 | 104 | .doccomment { 105 | padding-left: 25px; 106 | } 107 | 108 | .doccomment h1, .doccomment h2, .doccomment h3, .doccomment h4, .doccomment h5, .doccomment h6 { 109 | color: #444444; 110 | } 111 | 112 | .snippet { 113 | margin: 10px; 114 | padding: 10px; 115 | background-color: #DDDDDD; 116 | }` 117 | -------------------------------------------------------------------------------- /src/semantic/break_next.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/ast" 5 | "github.com/ark-lang/ark/src/util" 6 | ) 7 | 8 | // TODO handle match/switch, if we need to 9 | 10 | type BreakAndNextCheck struct { 11 | nestedLoopCount map[*ast.Function]int 12 | functions []*ast.Function 13 | } 14 | 15 | func (_ BreakAndNextCheck) Name() string { return "break and next" } 16 | 17 | func (v *BreakAndNextCheck) Init(s *SemanticAnalyzer) { 18 | v.nestedLoopCount = make(map[*ast.Function]int) 19 | v.functions = nil 20 | } 21 | 22 | func (v *BreakAndNextCheck) EnterScope(s *SemanticAnalyzer) {} 23 | func (v *BreakAndNextCheck) ExitScope(s *SemanticAnalyzer) {} 24 | func (v *BreakAndNextCheck) Finalize(s *SemanticAnalyzer) {} 25 | 26 | func (v *BreakAndNextCheck) Visit(s *SemanticAnalyzer, n ast.Node) { 27 | switch n := n.(type) { 28 | case *ast.NextStat, *ast.BreakStat: 29 | if v.nestedLoopCount[v.functions[len(v.functions)-1]] == 0 { 30 | s.Err(n, "%s must be in a loop", util.CapitalizeFirst(n.NodeName())) 31 | } 32 | 33 | case *ast.LoopStat: 34 | v.nestedLoopCount[v.functions[len(v.functions)-1]]++ 35 | 36 | case *ast.FunctionDecl: 37 | v.functions = append(v.functions, n.Function) 38 | case *ast.LambdaExpr: 39 | v.functions = append(v.functions, n.Function) 40 | } 41 | } 42 | 43 | func (v *BreakAndNextCheck) PostVisit(s *SemanticAnalyzer, n ast.Node) { 44 | switch n := n.(type) { 45 | case *ast.Block: 46 | for i, c := range n.Nodes { 47 | if i < len(n.Nodes)-1 && isBreakOrNext(c) { 48 | s.Err(n.Nodes[i+1], "Unreachable code") 49 | } 50 | } 51 | 52 | case *ast.LoopStat: 53 | v.nestedLoopCount[v.functions[len(v.functions)-1]]-- 54 | case *ast.FunctionDecl: 55 | v.functions = v.functions[:len(v.functions)-1] 56 | delete(v.nestedLoopCount, n.Function) 57 | case *ast.LambdaExpr: 58 | v.functions = v.functions[:len(v.functions)-1] 59 | delete(v.nestedLoopCount, n.Function) 60 | } 61 | } 62 | 63 | func isBreakOrNext(n ast.Node) bool { 64 | switch n.(type) { 65 | case *ast.BreakStat, *ast.NextStat: 66 | return true 67 | } 68 | return false 69 | } 70 | -------------------------------------------------------------------------------- /src/semantic/unused.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/ast" 5 | ) 6 | 7 | type UnusedCheck struct { 8 | encountered []interface{} 9 | encounteredDecl []ast.Node 10 | uses map[interface{}]int 11 | } 12 | 13 | func (_ UnusedCheck) Name() string { return "unused" } 14 | 15 | func (v *UnusedCheck) Init(s *SemanticAnalyzer) { 16 | v.uses = make(map[interface{}]int) 17 | v.encountered = nil 18 | v.encounteredDecl = nil 19 | } 20 | 21 | func (v *UnusedCheck) EnterScope(s *SemanticAnalyzer) {} 22 | func (v *UnusedCheck) ExitScope(s *SemanticAnalyzer) {} 23 | func (v *UnusedCheck) PostVisit(s *SemanticAnalyzer, n ast.Node) {} 24 | 25 | func (v *UnusedCheck) Visit(s *SemanticAnalyzer, n ast.Node) { 26 | switch n := n.(type) { 27 | case *ast.VariableDecl: 28 | if !n.IsPublic() { 29 | v.encountered = append(v.encountered, n.Variable) 30 | v.encounteredDecl = append(v.encounteredDecl, n) 31 | } 32 | 33 | case *ast.DestructVarDecl: 34 | if !n.IsPublic() { 35 | for idx, vari := range n.Variables { 36 | if !n.ShouldDiscard[idx] { 37 | v.encountered = append(v.encountered, vari) 38 | v.encounteredDecl = append(v.encounteredDecl, n) 39 | } 40 | } 41 | } 42 | 43 | case *ast.FunctionDecl: 44 | if !n.IsPublic() { 45 | v.encountered = append(v.encountered, n.Function) 46 | v.encounteredDecl = append(v.encounteredDecl, n) 47 | } 48 | } 49 | 50 | switch n := n.(type) { 51 | case *ast.FunctionAccessExpr: 52 | v.uses[n.Function]++ 53 | 54 | case *ast.VariableAccessExpr: 55 | v.uses[n.Variable]++ 56 | } 57 | } 58 | 59 | func (v *UnusedCheck) Finalize(s *SemanticAnalyzer) { 60 | v.AnalyzeUsage(s) 61 | } 62 | 63 | func (v *UnusedCheck) AnalyzeUsage(s *SemanticAnalyzer) { 64 | for idx, it := range v.encountered { 65 | decl := v.encounteredDecl[idx] 66 | switch it := it.(type) { 67 | case *ast.Variable: 68 | if !it.IsImplicit && v.uses[it] == 0 { 69 | s.Warn(decl, "Unused variable `%s`", it.Name) 70 | } 71 | 72 | case *ast.Function: 73 | if v.uses[it] == 0 { 74 | s.Warn(decl, "Unused function `%s`", it.Name) 75 | } 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /src/parser/keywords.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | const ( 4 | KEYWORD_AS string = "as" 5 | KEYWORD_BREAK string = "break" 6 | KEYWORD_C string = "C" 7 | KEYWORD_DEFER string = "defer" 8 | KEYWORD_DISCARD string = "_" 9 | KEYWORD_DO string = "do" 10 | KEYWORD_ELSE string = "else" 11 | KEYWORD_ENUM string = "enum" 12 | KEYWORD_FALSE string = "false" 13 | KEYWORD_FOR string = "for" 14 | KEYWORD_FUNC string = "func" 15 | KEYWORD_LEN string = "len" 16 | KEYWORD_IF string = "if" 17 | KEYWORD_MATCH string = "match" 18 | KEYWORD_MUT string = "mut" 19 | KEYWORD_NEXT string = "next" 20 | KEYWORD_PUB string = "pub" 21 | KEYWORD_RETURN string = "return" 22 | KEYWORD_SIZEOF string = "sizeof" 23 | KEYWORD_STRUCT string = "struct" 24 | KEYWORD_INTERFACE string = "interface" 25 | KEYWORD_TRUE string = "true" 26 | KEYWORD_USE string = "use" 27 | KEYWORD_VOID string = "void" 28 | ) 29 | 30 | var keywordList = []string{ 31 | KEYWORD_AS, 32 | KEYWORD_BREAK, 33 | KEYWORD_C, 34 | KEYWORD_DEFER, 35 | KEYWORD_DISCARD, 36 | KEYWORD_DO, 37 | KEYWORD_ELSE, 38 | KEYWORD_ENUM, 39 | KEYWORD_FALSE, 40 | KEYWORD_FOR, 41 | KEYWORD_FUNC, 42 | KEYWORD_LEN, 43 | KEYWORD_IF, 44 | KEYWORD_MATCH, 45 | KEYWORD_MUT, 46 | KEYWORD_NEXT, 47 | KEYWORD_PUB, 48 | KEYWORD_RETURN, 49 | KEYWORD_SIZEOF, 50 | KEYWORD_STRUCT, 51 | KEYWORD_INTERFACE, 52 | KEYWORD_TRUE, 53 | KEYWORD_USE, 54 | KEYWORD_VOID, 55 | } 56 | 57 | // Contains a map with all keywords as keys, and true as values 58 | // Uses a map for quick lookup time when checking for reserved vars 59 | var keywordMap map[string]bool 60 | 61 | func init() { 62 | keywordMap = make(map[string]bool) 63 | 64 | for _, key := range keywordList { 65 | keywordMap[key] = true 66 | } 67 | } 68 | 69 | func IsReservedKeyword(s string) bool { 70 | if m := keywordMap[s]; m { 71 | return true 72 | } 73 | 74 | // names starting with a _ followed by an uppercase letter 75 | // are reserved as they can interfere with name mangling 76 | if len(s) >= 2 && s[0] == '_' && (s[1] >= 'A' && s[1] <= 'Z') { 77 | return true 78 | } 79 | 80 | return false 81 | } 82 | -------------------------------------------------------------------------------- /src/cmd/ark/runtime.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/ast" 5 | "github.com/ark-lang/ark/src/lexer" 6 | "github.com/ark-lang/ark/src/parser" 7 | "github.com/ark-lang/ark/src/semantic" 8 | ) 9 | 10 | // TODO: Move this at a file and handle locating/specifying this file 11 | const RuntimeSource = ` 12 | #[c] func printf(fmt: ^u8, ...) -> int; 13 | #[c] func exit(code: C::int); 14 | 15 | pub func panic(message: string) { 16 | if len(message) == 0 { 17 | C::printf(c"\n"); 18 | } else { 19 | C::printf(c"panic: %.*s\n", len(message), &message[0]); 20 | } 21 | C::exit(-1); 22 | } 23 | 24 | pub type Option enum { 25 | Some(T), 26 | None, 27 | }; 28 | 29 | pub func (o: Option) unwrap() -> T { 30 | match o { 31 | Some(t) => return t, 32 | None => panic("Option.unwrap: expected Some, have None"), 33 | } 34 | 35 | a: T; 36 | return a; 37 | } 38 | 39 | type RawArray struct { 40 | size: uint, 41 | ptr: uintptr, 42 | }; 43 | 44 | pub func makeArray(ptr: ^T, size: uint) -> []T { 45 | raw := RawArray{size: size, ptr: uintptr(ptr)}; 46 | return @(^[]T)(uintptr(^raw)); 47 | } 48 | 49 | pub func breakArray(arr: []T) -> (uint, ^T) { 50 | raw := @(^RawArray)(uintptr(^arr)); 51 | return (raw.size, (^T)(raw.ptr)); 52 | } 53 | ` 54 | 55 | func LoadRuntime() *ast.Module { 56 | runtimeModule := &ast.Module{ 57 | Name: &ast.ModuleName{ 58 | Parts: []string{"__runtime"}, 59 | }, 60 | Dirpath: "__runtime", 61 | Parts: make(map[string]*ast.Submodule), 62 | } 63 | 64 | sourcefile := &lexer.Sourcefile{ 65 | Name: "runtime", 66 | Path: "runtime.ark", 67 | Contents: []rune(RuntimeSource), 68 | NewLines: []int{-1, -1}, 69 | } 70 | lexer.Lex(sourcefile) 71 | 72 | tree, deps := parser.Parse(sourcefile) 73 | if len(deps) > 0 { 74 | panic("INTERNAL ERROR: No dependencies allowed in runtime") 75 | } 76 | runtimeModule.Trees = append(runtimeModule.Trees, tree) 77 | 78 | ast.Construct(runtimeModule, nil) 79 | ast.Resolve(runtimeModule, nil) 80 | 81 | for _, submod := range runtimeModule.Parts { 82 | ast.Infer(submod) 83 | } 84 | 85 | semantic.SemCheck(runtimeModule, *ignoreUnused) 86 | 87 | ast.LoadRuntimeModule(runtimeModule) 88 | 89 | return runtimeModule 90 | } 91 | -------------------------------------------------------------------------------- /lib/std/rand/pcg.ark: -------------------------------------------------------------------------------- 1 | type pcg_state_setseq_64 struct { 2 | /// RNG state. All values possible. 3 | state: u64, 4 | /// Controls which RNG sequence (stream) is selected. Must *always* be odd. 5 | inc: u64, 6 | }; 7 | 8 | pub type Pcg32Random pcg_state_setseq_64; 9 | 10 | /// Seed the rng. Specified in two parts, state initializer and a 11 | /// sequence selection constant (a.k.a. stream id) 12 | pub func (rng: ^mut Pcg32Random) seed(initstate: u64, initseq: u64) { 13 | rng.state = 0; 14 | rng.inc = (initseq << 1) | 1; 15 | rng.random(); 16 | rng.state += initstate; 17 | rng.random(); 18 | } 19 | 20 | /// Generate a uniformly distributed 32-bit random number 21 | pub func (rng: ^mut Pcg32Random) random() -> u32 { 22 | oldstate := rng.state; 23 | rng.state = oldstate * 6364136223846793005 + rng.inc; 24 | xorshifted := u32(((oldstate >> 18) ^ oldstate) >> 27); 25 | rot := u32(oldstate >> 59); 26 | return (xorshifted >> rot) | (xorshifted << ((-rot) & 31)); 27 | } 28 | 29 | 30 | /// Generate a uniformly distributed number, r, where 0 <= r < bound 31 | pub func (pcg: ^mut Pcg32Random) boundedRand(bound: u32) -> u32 { 32 | // To avoid bias, we need to make the range of the RNG a multiple of 33 | // bound, which we do by dropping output less than a threshold. 34 | // A naive scheme to calculate the threshold would be to do 35 | // 36 | // uint32_t threshold = 0x100000000ull % bound; 37 | // 38 | // but 64-bit div/mod is slower than 32-bit div/mod (especially on 39 | // 32-bit platforms). In essence, we do 40 | // 41 | // uint32_t threshold = (0x100000000ull-bound) % bound; 42 | // 43 | // because this version will calculate the same modulus, but the LHS 44 | // value is less than 2^32. 45 | 46 | threshold := -bound % bound; 47 | 48 | // Uniformity guarantees that this loop will terminate. In practice, it 49 | // should usually terminate quickly; on average (assuming all bounds are 50 | // equally likely), 82.25% of the time, we can expect it to require just 51 | // one iteration. In the worst case, someone passes a bound of 2^31 + 1 52 | // (i.e., 2147483649), which invalidates almost 50% of the range. In 53 | // practice, bounds are typically small and only a tiny amount of the range 54 | // is eliminated. 55 | for { 56 | r := pcg.random(); 57 | if (r >= threshold) { 58 | return r % bound; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /src/doc/doc.go: -------------------------------------------------------------------------------- 1 | package doc 2 | 3 | import ( 4 | "os" 5 | "time" 6 | 7 | "github.com/ark-lang/ark/src/ast" 8 | "github.com/ark-lang/ark/src/parser" 9 | "github.com/ark-lang/ark/src/util" 10 | "github.com/ark-lang/ark/src/util/log" 11 | ) 12 | 13 | type Docgen struct { 14 | Input []*ast.Module 15 | Dir string 16 | 17 | output []*File 18 | curOutput *File 19 | } 20 | 21 | func (v *Docgen) Generate() { 22 | log.Verboseln("docgen", util.TEXT_BOLD+util.TEXT_GREEN+"Started docgenning"+util.TEXT_RESET) 23 | t := time.Now() 24 | 25 | v.output = make([]*File, 0) 26 | 27 | v.traverse() 28 | 29 | v.generate() 30 | 31 | dur := time.Since(t) 32 | log.Verbose("docgen", util.TEXT_BOLD+util.TEXT_GREEN+"Finished docgenning"+util.TEXT_RESET+" (%.2fms)\n", 33 | float32(dur.Nanoseconds())/1000000) 34 | } 35 | 36 | func (v *Docgen) traverse() { 37 | for _, file := range v.Input { 38 | v.curOutput = &File{ 39 | // XXX: This might cause problems on windows (`:` not allowed in file names) 40 | Name: file.Name.String(), 41 | } 42 | 43 | for _, submod := range file.Parts { 44 | for _, n := range submod.Nodes { 45 | switch n.(type) { 46 | case ast.Decl: 47 | decl := &Decl{ 48 | Node: n.(parser.Documentable), 49 | } 50 | 51 | for _, comm := range decl.Node.DocComments() { 52 | decl.Docs += comm.Contents + "\n" 53 | } 54 | 55 | decl.process() 56 | 57 | switch n.(type) { 58 | case *ast.FunctionDecl: 59 | v.curOutput.FunctionDecls = append(v.curOutput.FunctionDecls, decl) 60 | //case *ast.StructDecl: 61 | // v.curOutput.StructDecls = append(v.curOutput.StructDecls, decl) 62 | //case *ast.TraitDecl: 63 | // v.curOutput.TraitDecls = append(v.curOutput.TraitDecls, decl) 64 | //case *ast.ImplDecl: 65 | // v.curOutput.ImplDecls = append(v.curOutput.ImplDecls, decl) 66 | case *ast.VariableDecl: 67 | v.curOutput.VariableDecls = append(v.curOutput.VariableDecls, decl) 68 | default: 69 | panic("dammit") 70 | } 71 | } 72 | } 73 | } 74 | 75 | v.output = append(v.output, v.curOutput) 76 | v.curOutput = nil 77 | } 78 | } 79 | 80 | func (v *Docgen) generate() { 81 | if v.Dir[len(v.Dir)-1] != '/' { 82 | v.Dir += "/" 83 | } 84 | 85 | err := os.MkdirAll(v.Dir+"files", os.ModeDir|0777) 86 | if err != nil { 87 | panic(err) 88 | } 89 | 90 | v.generateStyle() 91 | v.generateIndex() 92 | 93 | for _, outputFile := range v.output { 94 | v.generateFile(outputFile) 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /lib/std/io/file.ark: -------------------------------------------------------------------------------- 1 | #use std::mem 2 | #use std::strings 3 | #use std::unicode::utf8 4 | 5 | pub type FileMode enum { 6 | Read, 7 | Write, 8 | Append, 9 | }; 10 | 11 | pub type File struct { 12 | name: string, 13 | path: ^u8, 14 | handle: ^FILE, 15 | }; 16 | 17 | SEEK_END: uint = 2; 18 | 19 | READ_FLAG: ^u8 = c"r"; 20 | WRITE_FLAG: ^u8 = c"w"; 21 | APPEND_FLAG: ^u8 = c"a"; 22 | 23 | pub func (File) open(path: ^u8, mode: FileMode) -> Option<^mut File> { 24 | mut f: ^mut File = mem::alloc(); 25 | f.path = path; 26 | f.handle = C::fopen(path, mode.getLegacyFileMode()); 27 | 28 | if f.handle == (^FILE)(0) { 29 | // failed to read 30 | return Option::None; 31 | } 32 | 33 | return Option::Some(f); 34 | } 35 | 36 | pub func (f: ^mut File) readToString() -> Option<^mut strings::String> { 37 | if !f.isOpen() { 38 | return Option::None; 39 | } 40 | 41 | if C::fseek(f.handle, 0, SEEK_END) != 0 { 42 | panic("failed to seek"); 43 | } 44 | 45 | size := C::ftell(f.handle); 46 | if size == -1 { 47 | panic("failed to tell"); 48 | } 49 | 50 | C::rewind(f.handle); 51 | 52 | result: ^mut strings::String = strings::String::withCapacity(uint(size)); 53 | 54 | mut idx := uint(0); 55 | for idx < uint(size) { 56 | char := []u8{C::fgetc(f.handle)}; 57 | decodedChar := []rune{0}; 58 | utf8::decode(decodedChar, char); 59 | result.append(decodedChar[0]); 60 | idx += 1; 61 | } 62 | 63 | return Option::Some(result); 64 | } 65 | 66 | pub func (f: ^mut File) isOpen() -> bool { 67 | return f.handle != (^FILE)(0); 68 | } 69 | 70 | pub func (f: ^mut File) destroy() { 71 | if f.isOpen() { 72 | C::fclose(f.handle); 73 | } 74 | mem::free(f); 75 | } 76 | 77 | /* 78 | Note this is just placeholder stuff... 79 | none of this really means anything and 80 | most of these are somewhat arbitrarily 81 | chosen... lol 82 | */ 83 | pub func (f: FileMode) getLegacyFileMode() -> ^u8 { 84 | match f { 85 | FileMode::Read => { 86 | return READ_FLAG; 87 | }, 88 | FileMode::Write => { 89 | return WRITE_FLAG; 90 | }, 91 | FileMode::Append => { 92 | return APPEND_FLAG; 93 | }, 94 | _ => { 95 | return READ_FLAG; 96 | }, 97 | } 98 | return READ_FLAG; 99 | } 100 | 101 | // C stuff for 102 | // loading files 103 | 104 | type FILE struct {}; 105 | 106 | #[c] func fopen(loc: ^u8, mode: ^u8) -> ^FILE; 107 | #[c] func fseek(handle: ^FILE, offset: uint, whence: uint) -> int; 108 | #[c] func ftell(handle: ^FILE) -> int; 109 | #[c] func rewind(handle: ^FILE); 110 | #[c] func fread(where: ^u8, sz: uint, dunno: uint, handle: ^FILE); 111 | #[c] func fclose(handle: ^FILE); 112 | #[c] func getenv(what: ^u8) -> ^u8; 113 | #[c] func fgetc(handle: ^FILE) -> u8; -------------------------------------------------------------------------------- /src/semantic/unreachable.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import "github.com/ark-lang/ark/src/ast" 4 | 5 | type UnreachableCheck struct { 6 | } 7 | 8 | func (_ UnreachableCheck) Name() string { return "unreachable" } 9 | 10 | func (v *UnreachableCheck) Init(s *SemanticAnalyzer) {} 11 | func (v *UnreachableCheck) EnterScope(s *SemanticAnalyzer) {} 12 | func (v *UnreachableCheck) ExitScope(s *SemanticAnalyzer) {} 13 | 14 | func (v *UnreachableCheck) Visit(s *SemanticAnalyzer, n ast.Node) {} 15 | 16 | func (v *UnreachableCheck) PostVisit(s *SemanticAnalyzer, n ast.Node) { 17 | switch n := n.(type) { 18 | case *ast.Block: 19 | for i, c := range n.Nodes { 20 | if i < len(n.Nodes)-1 && IsNodeTerminating(c) { 21 | s.Err(n.Nodes[i+1], "Unreachable code") 22 | } 23 | } 24 | 25 | if len(n.Nodes) > 0 { 26 | n.IsTerminating = IsNodeTerminating(n.Nodes[len(n.Nodes)-1]) 27 | } 28 | 29 | case *ast.FunctionDecl: 30 | v.visitFunction(s, n, n.Function) 31 | 32 | case *ast.LambdaExpr: 33 | v.visitFunction(s, n, n.Function) 34 | } 35 | 36 | } 37 | 38 | func (v *UnreachableCheck) visitFunction(s *SemanticAnalyzer, loc ast.Locatable, fn *ast.Function) { 39 | if fn.Body != nil && !fn.Body.IsTerminating { 40 | if fn.Type.Return != nil && !fn.Type.Return.BaseType.ActualType().IsVoidType() { 41 | s.Err(loc, "Missing return statement") 42 | } else { 43 | fn.Body.Nodes = append(fn.Body.Nodes, &ast.ReturnStat{}) 44 | fn.Body.IsTerminating = true 45 | } 46 | } 47 | } 48 | 49 | func (v *UnreachableCheck) Finalize(s *SemanticAnalyzer) { 50 | 51 | } 52 | 53 | type loopTerminatingChecker struct { 54 | nonTerminating bool 55 | } 56 | 57 | func (_ loopTerminatingChecker) EnterScope() {} 58 | func (_ loopTerminatingChecker) ExitScope() {} 59 | func (_ loopTerminatingChecker) PostVisit(n *ast.Node) {} 60 | 61 | // TODO account for labeled breaks 62 | func (v *loopTerminatingChecker) Visit(n *ast.Node) bool { 63 | if _, ok := (*n).(*ast.BreakStat); ok { 64 | v.nonTerminating = true 65 | return false 66 | } 67 | return true 68 | } 69 | 70 | func IsNodeTerminating(n ast.Node) bool { 71 | switch n := n.(type) { 72 | case *ast.Block: 73 | return n.IsTerminating 74 | case *ast.LoopStat: 75 | if n.LoopType == ast.LOOP_TYPE_INFINITE { 76 | checker := &loopTerminatingChecker{} 77 | vis := ast.NewASTVisitor(checker) 78 | vis.VisitBlock(n.Body) 79 | return !checker.nonTerminating 80 | } 81 | case *ast.ReturnStat: 82 | return true 83 | case *ast.IfStat: 84 | if n.Else == nil || n.Else != nil && !n.Else.IsTerminating { 85 | return false 86 | } 87 | 88 | for _, body := range n.Bodies { 89 | if !body.IsTerminating { 90 | return false 91 | } 92 | } 93 | 94 | return true 95 | } 96 | 97 | return false 98 | } 99 | -------------------------------------------------------------------------------- /src/semantic/recursive.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/ast" 5 | "github.com/ark-lang/ark/src/util/log" 6 | ) 7 | 8 | type RecursiveDefinitionCheck struct { 9 | } 10 | 11 | func (_ RecursiveDefinitionCheck) Name() string { return "recursive definition" } 12 | 13 | func (v *RecursiveDefinitionCheck) Init(s *SemanticAnalyzer) {} 14 | func (v *RecursiveDefinitionCheck) EnterScope(s *SemanticAnalyzer) {} 15 | func (v *RecursiveDefinitionCheck) ExitScope(s *SemanticAnalyzer) {} 16 | 17 | func (v *RecursiveDefinitionCheck) PostVisit(s *SemanticAnalyzer, n ast.Node) {} 18 | 19 | func (v *RecursiveDefinitionCheck) Visit(s *SemanticAnalyzer, n ast.Node) { 20 | if typeDecl, ok := n.(*ast.TypeDecl); ok { 21 | typ := typeDecl.NamedType 22 | if ok, path := isTypeRecursive(typ); ok { 23 | s.Err(n, "Encountered recursive type definition") 24 | 25 | log.Errorln("semantic", "Path taken:") 26 | for _, typ := range path { 27 | log.Error("semantic", typ.TypeName()) 28 | log.Error("semantic", " <- ") 29 | } 30 | log.Error("semantic", "%s\n\n", typ.TypeName()) 31 | } 32 | } 33 | } 34 | 35 | func (v *RecursiveDefinitionCheck) Finalize(s *SemanticAnalyzer) { 36 | 37 | } 38 | 39 | func isTypeRecursive(typ ast.Type) (bool, []ast.Type) { 40 | typ = typ.ActualType() 41 | 42 | var check func(current ast.Type, path *[]ast.Type, traversed map[ast.Type]bool) bool 43 | check = func(current ast.Type, path *[]ast.Type, traversed map[ast.Type]bool) bool { 44 | switch current.(type) { 45 | case *ast.NamedType: 46 | if traversed[current] { 47 | return true 48 | } 49 | } 50 | 51 | switch typ := current.(type) { 52 | case ast.StructType: 53 | for _, mem := range typ.Members { 54 | if check(mem.Type.BaseType, path, traversed) { 55 | *path = append(*path, mem.Type.BaseType) 56 | return true 57 | } 58 | } 59 | 60 | case ast.TupleType: 61 | for _, mem := range typ.Members { 62 | if check(mem.BaseType, path, traversed) { 63 | *path = append(*path, mem.BaseType) 64 | return true 65 | } 66 | } 67 | 68 | case ast.EnumType: 69 | for _, mem := range typ.Members { 70 | if check(mem.Type, path, traversed) { 71 | *path = append(*path, mem.Type) 72 | return true 73 | } 74 | } 75 | 76 | case *ast.NamedType: 77 | traversed[current] = true 78 | if check(typ.Type, path, traversed) { 79 | *path = append(*path, typ.Type) 80 | return true 81 | } 82 | traversed[current] = false 83 | 84 | case ast.ArrayType: 85 | if typ.IsFixedLength && check(typ.MemberType.BaseType, path, traversed) { 86 | *path = append(*path, typ.MemberType.BaseType) 87 | return true 88 | } 89 | } 90 | return false 91 | } 92 | 93 | var path []ast.Type 94 | return check(typ, &path, make(map[ast.Type]bool)), path 95 | } 96 | -------------------------------------------------------------------------------- /src/util/log/log.go: -------------------------------------------------------------------------------- 1 | package log 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/ark-lang/ark/src/util" 9 | ) 10 | 11 | type LogLevel int 12 | 13 | const ( 14 | LevelDebug LogLevel = iota 15 | LevelVerbose 16 | LevelInfo 17 | LevelWarning 18 | LevelError 19 | ) 20 | 21 | var LevelMap = map[string]LogLevel{ 22 | "debug": LevelDebug, 23 | "verbose": LevelVerbose, 24 | "info": LevelInfo, 25 | "warning": LevelWarning, 26 | "error": LevelError, 27 | } 28 | 29 | var currentLevel LogLevel 30 | var enabledTags map[string]bool 31 | var enableAll bool 32 | 33 | func init() { 34 | currentLevel = LevelInfo 35 | enabledTags = make(map[string]bool) 36 | enableAll = false 37 | } 38 | 39 | func SetLevel(level string) { 40 | lvl, ok := LevelMap[level] 41 | if !ok { 42 | fmt.Println("Invalid log level") 43 | os.Exit(util.EXIT_FAILURE_SETUP) 44 | } 45 | 46 | currentLevel = lvl 47 | } 48 | 49 | func SetTags(tags string) { 50 | enabledTags = make(map[string]bool) 51 | enableAll = false 52 | 53 | for _, tag := range strings.Split(tags, ",") { 54 | if tag == "all" { 55 | enableAll = true 56 | } else { 57 | enabledTags[tag] = true 58 | } 59 | } 60 | } 61 | 62 | func AtLevel(level LogLevel) bool { 63 | return level >= currentLevel 64 | } 65 | 66 | func Log(level LogLevel, tag string, msg string, args ...interface{}) { 67 | if !enableAll { 68 | if !enabledTags[tag] { 69 | return 70 | } 71 | } 72 | 73 | if AtLevel(level) { 74 | fmt.Printf(msg, args...) 75 | } 76 | } 77 | 78 | func Logln(level LogLevel, tag string, msg string, args ...interface{}) { 79 | Log(level, tag, msg+"\n", args...) 80 | } 81 | 82 | func Debug(tag string, msg string, args ...interface{}) { 83 | Log(LevelDebug, tag, msg, args...) 84 | } 85 | 86 | func Debugln(tag string, msg string, args ...interface{}) { 87 | Logln(LevelDebug, tag, msg, args...) 88 | } 89 | 90 | func Verbose(tag string, msg string, args ...interface{}) { 91 | Log(LevelVerbose, tag, msg, args...) 92 | } 93 | 94 | func Verboseln(tag string, msg string, args ...interface{}) { 95 | Logln(LevelVerbose, tag, msg, args...) 96 | } 97 | 98 | func Info(tag string, msg string, args ...interface{}) { 99 | Log(LevelInfo, tag, msg, args...) 100 | } 101 | 102 | func Infoln(tag string, msg string, args ...interface{}) { 103 | Logln(LevelInfo, tag, msg, args...) 104 | } 105 | 106 | func Warning(tag string, msg string, args ...interface{}) { 107 | Log(LevelWarning, tag, msg, args...) 108 | } 109 | 110 | func Warningln(tag string, msg string, args ...interface{}) { 111 | Logln(LevelWarning, tag, msg, args...) 112 | } 113 | 114 | func Error(tag string, msg string, args ...interface{}) { 115 | Log(LevelError, tag, msg, args...) 116 | } 117 | 118 | func Errorln(tag string, msg string, args ...interface{}) { 119 | Logln(LevelError, tag, msg, args...) 120 | } 121 | -------------------------------------------------------------------------------- /src/codegen/LLVMCodegen/binary.go: -------------------------------------------------------------------------------- 1 | package LLVMCodegen 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "os/exec" 8 | 9 | "github.com/ark-lang/ark/src/ast" 10 | "github.com/ark-lang/ark/src/codegen" 11 | "github.com/ark-lang/ark/src/util/log" 12 | 13 | "github.com/ark-lang/go-llvm/llvm" 14 | ) 15 | 16 | func (v *Codegen) createIR(mod *WrappedModule) string { 17 | filename := v.OutputName + "-" + mod.MangledName(ast.MANGLE_ARK_UNSTABLE) + ".ll" 18 | 19 | err := ioutil.WriteFile(filename, []byte(mod.LlvmModule.String()), 0666) 20 | if err != nil { 21 | v.err("Couldn't write IR file "+filename+": `%s`", err.Error()) 22 | } 23 | 24 | return filename 25 | } 26 | 27 | func (v *Codegen) createObjectOrAssembly(mod *WrappedModule, typ llvm.CodeGenFileType) string { 28 | filename := v.OutputName + "-" + mod.MangledName(ast.MANGLE_ARK_UNSTABLE) 29 | if typ == llvm.AssemblyFile { 30 | filename += ".s" 31 | } else { 32 | filename += ".o" 33 | } 34 | 35 | membuf, err := v.targetMachine.EmitToMemoryBuffer(mod.LlvmModule, typ) 36 | if err != nil { 37 | v.err("Couldn't generate file "+filename+": `%s`", err.Error()) 38 | } 39 | 40 | err = ioutil.WriteFile(filename, membuf.Bytes(), 0666) 41 | if err != nil { 42 | v.err("Couldn't create file "+filename+": `%s`", err.Error()) 43 | } 44 | 45 | return filename 46 | } 47 | 48 | func (v *Codegen) createBinary() { 49 | if v.OutputType == codegen.OutputLLVMIR { 50 | for _, mod := range v.input { 51 | log.Timed("creating ir", mod.Name.String(), func() { 52 | v.createIR(mod) 53 | }) 54 | } 55 | return 56 | } else if v.OutputType == codegen.OutputAssembly { 57 | for _, mod := range v.input { 58 | log.Timed("creating assembly", mod.Name.String(), func() { 59 | v.createObjectOrAssembly(mod, llvm.AssemblyFile) 60 | }) 61 | } 62 | return 63 | } 64 | 65 | linkArgs := append(v.LinkerArgs, "-fno-PIE", "-nodefaultlibs", "-lc", "-lm") 66 | 67 | objFiles := []string{} 68 | 69 | for _, mod := range v.input { 70 | log.Timed("creating object", mod.Name.String(), func() { 71 | objName := v.createObjectOrAssembly(mod, llvm.ObjectFile) 72 | objFiles = append(objFiles, objName) 73 | linkArgs = append(linkArgs, objName) 74 | for _, lib := range mod.LinkedLibraries { 75 | linkArgs = append(linkArgs, fmt.Sprintf("-l%s", lib)) 76 | } 77 | }) 78 | } 79 | 80 | if v.OutputType == codegen.OutputObject { 81 | return 82 | } 83 | 84 | if v.OutputName == "" { 85 | panic("OutputName is empty") 86 | } 87 | 88 | linkArgs = append(linkArgs, "-o", v.OutputName) 89 | 90 | if v.Linker == "" { 91 | v.Linker = "cc" 92 | } 93 | 94 | log.Timed("linking", "", func() { 95 | log.Verboseln("codegen", "%s %v", v.Linker, linkArgs) 96 | 97 | cmd := exec.Command(v.Linker, linkArgs...) 98 | if out, err := cmd.CombinedOutput(); err != nil { 99 | v.err("failed to link object files: `%s`\n%s", err.Error(), string(out)) 100 | } 101 | }) 102 | 103 | for _, objFile := range objFiles { 104 | os.Remove(objFile) 105 | } 106 | } 107 | -------------------------------------------------------------------------------- /lib/std/strings/util.ark: -------------------------------------------------------------------------------- 1 | /// A set of utility functions 2 | /// for static strings. 3 | 4 | /// Returns if the given string 5 | /// contains the given value. The 6 | /// haystack is the string to look 7 | /// inside of for the needle. 8 | /// 9 | /// ## Example 10 | /// 11 | /// ``` 12 | /// #use std::string 13 | /// #use std::io 14 | /// 15 | /// pub func main() -> int { 16 | /// a := "foobar"; 17 | /// b := "bar"; 18 | /// if string::contains(a, b) { 19 | /// io::println("Found it!"); 20 | /// } 21 | /// return 0; 22 | /// } 23 | /// ``` 24 | pub func contains(haystack: string, needle: string) -> bool { 25 | if len(haystack) < len(needle) { 26 | return false; 27 | } 28 | 29 | mut idx := uint(0); 30 | mut count := uint(0); 31 | for idx < len(haystack) { 32 | if haystack[idx] == needle[count] { 33 | count += 1; 34 | } else { 35 | count = 0; 36 | } 37 | 38 | if count == len(needle) { 39 | return true; 40 | } 41 | 42 | idx += 1; 43 | } 44 | 45 | return false; 46 | } 47 | 48 | /// Returns if the given string 49 | /// has the given prefix 50 | /// 51 | /// ## Example 52 | /// ``` 53 | /// #use std::string 54 | /// #use std::io 55 | /// 56 | /// pub func main() -> int { 57 | /// file := "image_temp"; 58 | /// prefix := "image"; 59 | /// if string::hasPrefix(file, prefix) { 60 | /// io::println("Yep!"); 61 | /// } 62 | /// return 0; 63 | /// } 64 | /// ``` 65 | pub func hasPrefix(source: string, prefix: string) -> bool { 66 | if len(prefix) > len(source) { 67 | return false; 68 | } 69 | mut idx := uint(0); 70 | for idx < len(prefix) { 71 | if source[idx] != prefix[idx] { 72 | return false; 73 | } 74 | idx += 1; 75 | } 76 | return true; 77 | } 78 | 79 | /// Returns if the given string 80 | /// ends with the given value 81 | /// 82 | /// ## Example 83 | /// ``` 84 | /// #use std::string 85 | /// #use std::io 86 | /// 87 | /// pub func main() -> int { 88 | /// file := "main.ark"; 89 | /// if string::hasPrefix(file, ext) { 90 | /// io::println("Yep!"); 91 | /// } 92 | /// return 0; 93 | /// } 94 | /// ``` 95 | pub func hasSuffix(source: string, suffix: string) -> bool { 96 | 97 | if len(suffix) > len(source) { 98 | return false; 99 | } 100 | 101 | mut idx := len(source) - len(suffix); 102 | // im too tired to know why this works 103 | // but it works 104 | for idx < len(suffix) - idx { 105 | if source[idx] != suffix[idx] { 106 | return false; 107 | } 108 | idx += 1; 109 | } 110 | return true; 111 | } 112 | 113 | /// Compares two strings character 114 | /// for character. 115 | /// 116 | /// ## Example 117 | /// ``` 118 | /// #use std::string 119 | /// #use std::io 120 | /// 121 | /// pub func main() -> int { 122 | /// another := "hello, world"; 123 | /// one := "dj khaled"; 124 | /// if string::compare(another, one) { 125 | /// io::println("They are the same!"); 126 | /// } 127 | /// return 0; 128 | /// } 129 | /// ``` 130 | pub func compare(a: string, b: string) -> bool { 131 | if len(a) != len(b) { 132 | return false; 133 | } 134 | 135 | mut i := uint(0); 136 | for i < len(a) { 137 | if a[i] != b[i] { 138 | return false; 139 | } 140 | } 141 | 142 | return true; 143 | } -------------------------------------------------------------------------------- /src/ast/dependency.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "bytes" 5 | ) 6 | 7 | type DependencyNode struct { 8 | Module *ModuleName 9 | index int 10 | lowlink int 11 | onStack bool 12 | } 13 | 14 | type Dependency struct{ Src, Dst *DependencyNode } 15 | type NodeSet []*DependencyNode 16 | 17 | type DependencyGraph struct { 18 | Nodes NodeSet 19 | NodeIndices map[string]int 20 | EdgesFrom map[string][]Dependency 21 | 22 | index int 23 | stack []*DependencyNode 24 | out []NodeSet 25 | } 26 | 27 | func NewDependencyGraph() *DependencyGraph { 28 | return &DependencyGraph{ 29 | Nodes: make(NodeSet, 0), 30 | NodeIndices: make(map[string]int), 31 | EdgesFrom: make(map[string][]Dependency), 32 | } 33 | } 34 | 35 | func (v *DependencyGraph) getOrCreate(modname *ModuleName) *DependencyNode { 36 | idx, ok := v.NodeIndices[modname.String()] 37 | if !ok { 38 | idx = len(v.Nodes) 39 | v.Nodes = append(v.Nodes, &DependencyNode{Module: modname}) 40 | v.NodeIndices[modname.String()] = idx 41 | } 42 | return v.Nodes[idx] 43 | } 44 | 45 | func (v *DependencyGraph) AddDependency(source, dependency *ModuleName) { 46 | srcNode := v.getOrCreate(source) 47 | dstNode := v.getOrCreate(dependency) 48 | dep := Dependency{Src: srcNode, Dst: dstNode} 49 | v.EdgesFrom[source.String()] = append(v.EdgesFrom[source.String()], dep) 50 | } 51 | 52 | func (d *DependencyGraph) DetectCycles() []string { 53 | scgs := d.tarjan() 54 | 55 | var errs []string 56 | for _, scg := range scgs { 57 | if len(scg) == 1 { 58 | continue 59 | } 60 | 61 | buf := new(bytes.Buffer) 62 | for idx, v := range scg { 63 | buf.WriteString(v.Module.String()) 64 | if idx != len(scg)-1 { 65 | buf.WriteString(", ") 66 | } 67 | } 68 | errs = append(errs, buf.String()) 69 | } 70 | 71 | return errs 72 | } 73 | 74 | func (v *DependencyGraph) tarjan() []NodeSet { 75 | // Tarjan's strongly connected components algorithm, as per: 76 | // https://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm 77 | v.out = nil 78 | 79 | v.index = 0 80 | v.stack = nil 81 | 82 | // Initial clear 83 | for _, node := range v.Nodes { 84 | node.index, node.lowlink = -1, -1 85 | } 86 | 87 | // Actual algorithm run 88 | for _, node := range v.Nodes { 89 | if node.index == -1 { 90 | v.strongConnect(node) 91 | } 92 | } 93 | 94 | return v.out 95 | } 96 | 97 | func (d *DependencyGraph) strongConnect(v *DependencyNode) { 98 | v.index = d.index 99 | v.lowlink = d.index 100 | d.index++ 101 | 102 | d.stack = append(d.stack, v) 103 | v.onStack = true 104 | 105 | edges := d.EdgesFrom[v.Module.String()] 106 | for _, edge := range edges { 107 | w := edge.Dst 108 | if w.index == -1 { 109 | d.strongConnect(w) 110 | v.lowlink = min(v.lowlink, w.lowlink) 111 | } else if w.onStack { 112 | v.lowlink = min(v.lowlink, w.index) 113 | } 114 | } 115 | 116 | if v.lowlink == v.index { 117 | out := make(NodeSet, 0) 118 | for { 119 | idx := len(d.stack) - 1 120 | w := d.stack[idx] 121 | d.stack[idx] = nil 122 | d.stack = d.stack[:idx] 123 | 124 | w.onStack = false 125 | 126 | out = append(out, w) 127 | 128 | if w == v { 129 | break 130 | } 131 | } 132 | d.out = append(d.out, out) 133 | } 134 | } 135 | 136 | func min(a, b int) int { 137 | if a < b { 138 | return a 139 | } 140 | return b 141 | } 142 | -------------------------------------------------------------------------------- /src/doc/decl.go: -------------------------------------------------------------------------------- 1 | package doc 2 | 3 | import ( 4 | "html/template" 5 | 6 | "github.com/ark-lang/ark/src/parser" 7 | ) 8 | 9 | type Decl struct { 10 | Node parser.Documentable 11 | Docs string 12 | ParsedDocs template.HTML // docs after markdown parsing 13 | Ident string // identifier 14 | Snippet string // code snippet of declaration 15 | } 16 | 17 | func (v *Decl) process() { 18 | /*v.ParsedDocs = template.HTML(parseMarkdown(v.Docs)) 19 | 20 | switch v.Node.(type) { 21 | case *parser.FunctionDecl: 22 | v.Ident, v.Snippet = generateFunctionDeclSnippet(v.Node.(*parser.FunctionDecl)) 23 | case *parser.StructDecl: 24 | v.Ident, v.Snippet = generateStructDeclSnippet(v.Node.(*parser.StructDecl)) 25 | case *parser.TraitDecl: 26 | v.Ident, v.Snippet = generateTraitDeclSnippet(v.Node.(*parser.TraitDecl)) 27 | case *parser.ImplDecl: 28 | v.Ident, v.Snippet = generateImplDeclSnippet(v.Node.(*parser.ImplDecl)) 29 | case *parser.VariableDecl: 30 | v.Ident, v.Snippet = generateVariableDeclSnippet(v.Node.(*parser.VariableDecl)) 31 | default: 32 | panic("unimplimented decl type in doc") 33 | }*/ 34 | } 35 | 36 | /*func generateFunctionDeclSnippet(decl *parser.FunctionDecl) (ident, snippet string) { 37 | ident = decl.Function.Name 38 | 39 | snippet = parser.KEYWORD_FUNC + " " + ident + "(" 40 | for i, par := range decl.Function.Parameters { 41 | snippet += par.Variable.Name + ": " + par.Variable.Type.TypeName() 42 | if i < len(decl.Function.Parameters)-1 { 43 | snippet += ", " 44 | } 45 | } 46 | snippet += ")" 47 | if decl.Function.ReturnType != nil { 48 | snippet += ": " + decl.Function.ReturnType.TypeName() 49 | 50 | } 51 | 52 | return 53 | } 54 | 55 | func generateStructDeclSnippet(decl *parser.StructDecl) (ident, snippet string) { 56 | ident = decl.Struct.Name 57 | snippet = "struct " + decl.Struct.Name + " {" 58 | for i, member := range decl.Struct.Variables { 59 | snippet += "\n " + member.Variable.Name + ": " + member.Variable.Type.TypeName() 60 | if member.Assignment != nil { 61 | snippet += " = [TODO values]" 62 | } 63 | if i < len(decl.Struct.Variables)-1 { 64 | snippet += "," 65 | } else { 66 | snippet += "\n" 67 | } 68 | } 69 | snippet += "}" 70 | return 71 | } 72 | 73 | func generateTraitDeclSnippet(decl *parser.TraitDecl) (ident, snippet string) { 74 | ident = decl.Trait.Name 75 | snippet = "trait " + decl.Trait.Name + " {" 76 | for _, member := range decl.Trait.Functions { 77 | _, functionSnippet := generateFunctionDeclSnippet(member) 78 | snippet += "\n " + functionSnippet + "\n" 79 | } 80 | snippet += "}" 81 | return 82 | } 83 | 84 | func generateImplDeclSnippet(decl *parser.ImplDecl) (ident, snippet string) { 85 | ident = decl.StructName 86 | snippet = "impl " + decl.StructName 87 | if decl.TraitName != "" { 88 | snippet += " for " + decl.TraitName 89 | } 90 | snippet += " {" 91 | for _, member := range decl.Functions { 92 | _, functionSnippet := generateFunctionDeclSnippet(member) 93 | snippet += "\n " + functionSnippet + "\n" 94 | } 95 | snippet += "}" 96 | return 97 | } 98 | 99 | func generateVariableDeclSnippet(decl *parser.VariableDecl) (ident, snippet string) { 100 | ident = decl.Variable.Name 101 | snippet = ident + ": " + decl.Variable.Type.TypeName() 102 | if decl.Assignment != nil { 103 | snippet += " = [TODO values]" 104 | } 105 | return 106 | }*/ 107 | -------------------------------------------------------------------------------- /src/parser/operators.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | type OpCategory int 4 | 5 | const ( 6 | OP_ARITHMETIC OpCategory = iota 7 | OP_COMPARISON 8 | OP_BITWISE 9 | OP_LOGICAL 10 | ) 11 | 12 | func (v OpCategory) PrettyString() string { 13 | switch v { 14 | case OP_ARITHMETIC: 15 | return "arithmetic" 16 | case OP_COMPARISON: 17 | return "comparison" 18 | case OP_BITWISE: 19 | return "bitwise" 20 | case OP_LOGICAL: 21 | return "logical" 22 | default: 23 | panic("missing opcategory") 24 | } 25 | } 26 | 27 | //go:generate stringer -type=BinOpType 28 | type BinOpType int 29 | 30 | const ( 31 | BINOP_ERR BinOpType = iota 32 | 33 | BINOP_ADD 34 | BINOP_SUB 35 | BINOP_MUL 36 | BINOP_DIV 37 | BINOP_MOD 38 | 39 | BINOP_GREATER 40 | BINOP_LESS 41 | BINOP_GREATER_EQ 42 | BINOP_LESS_EQ 43 | BINOP_EQ 44 | BINOP_NOT_EQ 45 | 46 | BINOP_BIT_AND 47 | BINOP_BIT_OR 48 | BINOP_BIT_XOR 49 | BINOP_BIT_LEFT 50 | BINOP_BIT_RIGHT 51 | 52 | BINOP_LOG_AND 53 | BINOP_LOG_OR 54 | ) 55 | 56 | var binOpStrings = []string{"", "+", "-", "*", "/", "%", ">", "<", ">=", "<=", 57 | "==", "!=", "&", "|", "^", "<<", ">>", "&&", "||"} 58 | 59 | func stringToBinOpType(s string) BinOpType { 60 | for i, str := range binOpStrings { 61 | if str == s { 62 | return BinOpType(i) 63 | } 64 | } 65 | return BINOP_ERR 66 | } 67 | 68 | func (v BinOpType) OpString() string { 69 | return binOpStrings[v] 70 | } 71 | 72 | func (v BinOpType) Category() OpCategory { 73 | switch v { 74 | case BINOP_ADD, BINOP_SUB, BINOP_MUL, BINOP_DIV, BINOP_MOD: 75 | return OP_ARITHMETIC 76 | case BINOP_GREATER, BINOP_LESS, BINOP_GREATER_EQ, BINOP_LESS_EQ, BINOP_EQ, BINOP_NOT_EQ: 77 | return OP_COMPARISON 78 | case BINOP_BIT_AND, BINOP_BIT_OR, BINOP_BIT_XOR, BINOP_BIT_LEFT, BINOP_BIT_RIGHT: 79 | return OP_BITWISE 80 | case BINOP_LOG_AND, BINOP_LOG_OR: 81 | return OP_LOGICAL 82 | default: 83 | panic("missing op category") 84 | } 85 | } 86 | 87 | func newBinOpPrecedenceMap() map[BinOpType]int { 88 | m := make(map[BinOpType]int) 89 | 90 | // lowest to highest 91 | precedences := [][]BinOpType{ 92 | {BINOP_LOG_OR}, 93 | {BINOP_LOG_AND}, 94 | {BINOP_BIT_OR}, 95 | {BINOP_BIT_XOR}, 96 | {BINOP_BIT_AND}, 97 | {BINOP_EQ, BINOP_NOT_EQ}, 98 | {BINOP_GREATER, BINOP_LESS, BINOP_GREATER_EQ, BINOP_LESS_EQ}, 99 | {BINOP_BIT_LEFT, BINOP_BIT_RIGHT}, 100 | {BINOP_ADD, BINOP_SUB}, 101 | {BINOP_MUL, BINOP_DIV, BINOP_MOD}, 102 | } 103 | 104 | for i, list := range precedences { 105 | for _, op := range list { 106 | m[op] = i + 1 107 | } 108 | } 109 | 110 | return m 111 | } 112 | 113 | //go:generate stringer -type=UnOpType 114 | type UnOpType int 115 | 116 | const ( 117 | UNOP_ERR UnOpType = iota 118 | 119 | UNOP_LOG_NOT 120 | 121 | UNOP_BIT_NOT 122 | 123 | UNOP_NEGATIVE 124 | 125 | UNOP_DEREF 126 | ) 127 | 128 | func stringToUnOpType(s string) UnOpType { 129 | switch s { 130 | case "!": 131 | return UNOP_LOG_NOT 132 | 133 | case "~": 134 | return UNOP_BIT_NOT 135 | 136 | case "-": 137 | return UNOP_NEGATIVE 138 | 139 | case "@": 140 | return UNOP_DEREF 141 | 142 | default: 143 | return UNOP_ERR 144 | } 145 | } 146 | 147 | func (v UnOpType) OpString() string { 148 | switch v { 149 | case UNOP_LOG_NOT: 150 | return "!" 151 | 152 | case UNOP_BIT_NOT: 153 | return "~" 154 | 155 | case UNOP_NEGATIVE: 156 | return "-" 157 | 158 | case UNOP_DEREF: 159 | return "@" 160 | 161 | default: 162 | return "ERR" 163 | } 164 | } 165 | -------------------------------------------------------------------------------- /lib/std/io/println.ark: -------------------------------------------------------------------------------- 1 | #use std::unicode::utf8 2 | #use std::strings 3 | #use std::mem 4 | 5 | #[c] func printf(fmt: ^u8, ...) -> int; 6 | 7 | pub func printNullTerminatedString(str: ^u8) -> int { 8 | return C::printf(c"%s\n", str); 9 | } 10 | 11 | /// Prints the given static string 12 | /// to standard out, with a newline 13 | /// 14 | /// ## Examples 15 | /// 16 | /// ``` 17 | /// #use std::io 18 | /// pub func main() -> int { 19 | /// io::println("Hello, World!"); 20 | /// return 0; 21 | /// } 22 | /// ``` 23 | pub func println(message: string) -> int { 24 | if len(message) == 0 { 25 | return C::printf(c"\n"); 26 | } 27 | return C::printf(c"%.*s\n", len(message), &message[0]); 28 | } 29 | 30 | /// Prints the given static string 31 | /// to standard out. Does not write 32 | /// a new line. 33 | /// 34 | /// ## Examples 35 | /// 36 | /// ``` 37 | /// #use std::io 38 | /// pub func main() -> int { 39 | /// io::print("hello "); 40 | /// io::print("world\n"); 41 | /// return 0; 42 | /// } 43 | /// ``` 44 | pub func print(message: string) -> int { 45 | if len(message) == 0 { 46 | return 0; 47 | } 48 | return C::printf(c"%.*s", len(message), &message[0]); 49 | } 50 | 51 | /// Prints a dynamic, heap allocated string 52 | /// to the standard out 53 | /// 54 | /// ## Examples 55 | /// 56 | /// ``` 57 | /// #use std::io 58 | /// #use std::string 59 | /// 60 | /// pub func main() -> int { 61 | /// name := strings::String::from("Felix"); 62 | /// defer name.destroy(); 63 | /// io::printDynamicString(name); 64 | /// return 0; 65 | /// } 66 | /// ``` 67 | pub func printDynamicString(s: ^strings::String) -> int { 68 | mut idx := uint(0); 69 | for idx < s.length() { 70 | valAtIdx := s.get(idx); 71 | printRune(valAtIdx.unwrap()); 72 | idx += 1; 73 | } 74 | printRune('\n'); 75 | return int(idx); 76 | } 77 | 78 | /// Prints a rune character to 79 | /// standard output 80 | /// 81 | /// ## Examples 82 | /// 83 | /// ``` 84 | /// #use std::io 85 | /// 86 | /// pub func main() -> int { 87 | /// io::printRune('A'); 88 | /// return 0; 89 | /// } 90 | /// ``` 91 | pub func printRune(r: rune) -> int { 92 | rArray := []rune{r}; 93 | bArray := []u8{0, 0, 0, 0}; 94 | utf8::encode(bArray, rArray); 95 | numBytes := utf8::numBytes(rArray); 96 | C::printf(c"%.*s", numBytes, &bArray[0]); 97 | return 0; 98 | } 99 | 100 | /// Prints an unsigned 8 bit integer (byte) 101 | /// to standard output 102 | /// 103 | /// ## Examples 104 | /// 105 | /// ``` 106 | /// #use std::io 107 | /// pub func main() -> int { 108 | /// io::printByte(u8(32)); 109 | /// return 0; 110 | /// } 111 | /// ``` 112 | pub func printByte(byte: u8) -> int { 113 | return C::printf(c"%c", byte); 114 | } 115 | 116 | /// Prints a signed register-sized integer 117 | /// to standard output 118 | /// 119 | /// ## Examples 120 | /// 121 | /// ``` 122 | /// #use std::io 123 | /// pub func main() -> int { 124 | /// io::printInt(32); 125 | /// return 0; 126 | /// } 127 | /// ``` 128 | pub func printInt(num: int) -> int { 129 | // use z for register-sized integer 130 | return C::printf(c"%zd", num); 131 | } 132 | 133 | /// Prints an unsigned integer to 134 | /// standard output. 135 | /// 136 | /// ## Examples 137 | /// 138 | /// ``` 139 | /// #use std::io 140 | /// pub func main() -> int { 141 | /// io::printUint(uint(32)); 142 | /// return 0; 143 | /// } 144 | /// ``` 145 | pub func printUint(num: uint) -> int { 146 | // use z for register-sized integer 147 | return C::printf(c"%zu", num); 148 | } 149 | -------------------------------------------------------------------------------- /src/lexer/sourcefile.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "bytes" 5 | "io/ioutil" 6 | "path" 7 | "strings" 8 | 9 | "github.com/ark-lang/ark/src/util" 10 | ) 11 | 12 | type Sourcefile struct { 13 | Path string 14 | Name string 15 | Contents []rune 16 | NewLines []int 17 | Tokens []*Token 18 | } 19 | 20 | func NewSourcefile(filepath string) (*Sourcefile, error) { 21 | // TODO, get this to handle the rare //file//shit 22 | // cut out the filename from path 23 | // + 1 to cut out the slash. 24 | i, j := strings.LastIndex(filepath, "/")+1, strings.LastIndex(filepath, path.Ext(filepath)) 25 | 26 | // this is the name of the file, not the path 27 | name := filepath[i:j] 28 | 29 | sf := &Sourcefile{Name: name, Path: filepath} 30 | sf.NewLines = append(sf.NewLines, -1) 31 | sf.NewLines = append(sf.NewLines, -1) 32 | 33 | contents, err := ioutil.ReadFile(sf.Path) 34 | if err != nil { 35 | return nil, err 36 | } 37 | 38 | sf.Contents = []rune(string(contents)) 39 | return sf, nil 40 | } 41 | 42 | func (s *Sourcefile) GetLine(line int) string { 43 | return string(s.Contents[s.NewLines[line]+1 : s.NewLines[line+1]]) 44 | } 45 | 46 | const TabWidth = 4 47 | 48 | func (s *Sourcefile) MarkPos(pos Position) string { 49 | buf := new(bytes.Buffer) 50 | 51 | lineString := s.GetLine(pos.Line) 52 | lineStringRunes := []rune(lineString) 53 | pad := pos.Char - 1 54 | 55 | buf.WriteString(strings.Replace(strings.Replace(lineString, "%", "%%", -1), "\t", " ", -1)) 56 | buf.WriteRune('\n') 57 | for i := 0; i < pad; i++ { 58 | spaces := 1 59 | 60 | if lineStringRunes[i] == '\t' { 61 | spaces = TabWidth 62 | } 63 | 64 | for t := 0; t < spaces; t++ { 65 | buf.WriteRune(' ') 66 | } 67 | } 68 | buf.WriteString(util.TEXT_GREEN + util.TEXT_BOLD + "^" + util.TEXT_RESET) 69 | buf.WriteRune('\n') 70 | 71 | return buf.String() 72 | 73 | } 74 | 75 | func (s *Sourcefile) MarkSpan(span Span) string { 76 | // if the span is just one character, use MarkPos instead 77 | spanEnd := span.End() 78 | spanEnd.Char-- 79 | if span.Start() == spanEnd { 80 | return s.MarkPos(span.Start()) 81 | } 82 | 83 | // mark the span 84 | buf := new(bytes.Buffer) 85 | 86 | for line := span.StartLine; line <= span.EndLine; line++ { 87 | lineString := s.GetLine(line) 88 | lineStringRunes := []rune(lineString) 89 | 90 | var pad int 91 | if line == span.StartLine { 92 | pad = span.StartChar - 1 93 | } else { 94 | pad = 0 95 | } 96 | 97 | var length int 98 | if line == span.EndLine { 99 | length = span.EndChar - span.StartChar 100 | } else { 101 | length = len(lineStringRunes) 102 | } 103 | 104 | buf.WriteString(strings.Replace(strings.Replace(lineString, "%", "%%", -1), "\t", " ", -1)) 105 | buf.WriteRune('\n') 106 | 107 | for i := 0; i < pad; i++ { 108 | spaces := 1 109 | 110 | if lineStringRunes[i] == '\t' { 111 | spaces = TabWidth 112 | } 113 | 114 | for t := 0; t < spaces; t++ { 115 | buf.WriteRune(' ') 116 | } 117 | } 118 | 119 | buf.WriteString(util.TEXT_GREEN + util.TEXT_BOLD) 120 | for i := 0; i < length; i++ { 121 | // there must be a less repetitive way to do this but oh well 122 | spaces := 1 123 | 124 | if lineStringRunes[i+pad] == '\t' { 125 | spaces = TabWidth 126 | } 127 | 128 | for t := 0; t < spaces; t++ { 129 | buf.WriteRune('~') 130 | } 131 | } 132 | buf.WriteString(util.TEXT_RESET) 133 | buf.WriteRune('\n') 134 | } 135 | 136 | return buf.String() 137 | } 138 | -------------------------------------------------------------------------------- /src/ast/module.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | "path/filepath" 7 | "strings" 8 | 9 | "github.com/ark-lang/ark/src/lexer" 10 | "github.com/ark-lang/ark/src/parser" 11 | "github.com/ark-lang/ark/src/util/log" 12 | ) 13 | 14 | type Module struct { 15 | Name *ModuleName 16 | ModScope *Scope 17 | Dirpath string 18 | Trees []*parser.ParseTree 19 | Parts map[string]*Submodule 20 | LinkedLibraries []string 21 | resolved bool 22 | } 23 | 24 | type Submodule struct { 25 | Parent *Module 26 | UseScope *Scope 27 | File *lexer.Sourcefile 28 | Nodes []Node 29 | inferred bool 30 | } 31 | 32 | type ModuleLookup struct { 33 | Name string 34 | Module *Module 35 | Children map[string]*ModuleLookup 36 | } 37 | 38 | func NewModuleLookup(name string) *ModuleLookup { 39 | res := &ModuleLookup{ 40 | Name: name, 41 | Children: make(map[string]*ModuleLookup), 42 | } 43 | return res 44 | } 45 | 46 | func (v *ModuleLookup) Get(name *ModuleName) (*ModuleLookup, error) { 47 | ml := v 48 | 49 | for idx, part := range name.Parts { 50 | nml, ok := ml.Children[part] 51 | if ok { 52 | ml = nml 53 | } else { 54 | return nil, fmt.Errorf("Module not found in lookup: %s", 55 | (&ModuleName{Parts: name.Parts[0 : idx+1]}).String()) 56 | } 57 | } 58 | 59 | return ml, nil 60 | } 61 | 62 | func (v *ModuleLookup) Create(name *ModuleName) *ModuleLookup { 63 | ml := v 64 | 65 | for _, part := range name.Parts { 66 | nml, ok := ml.Children[part] 67 | if !ok { 68 | nml = NewModuleLookup(part) 69 | ml.Children[part] = nml 70 | } 71 | ml = nml 72 | } 73 | 74 | return ml 75 | } 76 | 77 | func (v *ModuleLookup) Dump(i int) { 78 | if v.Name != "" { 79 | log.Debug("main", "%s", strings.Repeat(" ", i)) 80 | log.Debugln("main", "%s", v.Name) 81 | } 82 | 83 | for _, child := range v.Children { 84 | child.Dump(i + 1) 85 | } 86 | } 87 | 88 | type ModuleName struct { 89 | Parts []string 90 | } 91 | 92 | func NewModuleName(node *parser.NameNode) *ModuleName { 93 | res := &ModuleName{ 94 | Parts: make([]string, len(node.Modules)+1), 95 | } 96 | 97 | for idx, mod := range node.Modules { 98 | res.Parts[idx] = mod.Value 99 | } 100 | res.Parts[len(res.Parts)-1] = node.Name.Value 101 | 102 | return res 103 | } 104 | 105 | func JoinModuleName(modName *ModuleName, part string) *ModuleName { 106 | res := &ModuleName{ 107 | Parts: make([]string, len(modName.Parts)+1), 108 | } 109 | copy(res.Parts, modName.Parts) 110 | res.Parts[len(res.Parts)-1] = part 111 | return res 112 | } 113 | 114 | func ModuleNameFromUnresolvedName(unresName UnresolvedName) *ModuleName { 115 | res := &ModuleName{} 116 | res.Parts = append(res.Parts, unresName.ModuleNames...) 117 | res.Parts = append(res.Parts, unresName.Name) 118 | return res 119 | } 120 | 121 | func (v *ModuleName) Last() string { 122 | idx := len(v.Parts) - 1 123 | return v.Parts[idx] 124 | } 125 | 126 | func (v *ModuleName) String() string { 127 | buf := new(bytes.Buffer) 128 | for idx, parent := range v.Parts { 129 | buf.WriteString(parent) 130 | if idx < len(v.Parts)-1 { 131 | buf.WriteString("::") 132 | } 133 | 134 | } 135 | return buf.String() 136 | } 137 | 138 | func (v *ModuleName) ToPath() string { 139 | buf := new(bytes.Buffer) 140 | for idx, parent := range v.Parts { 141 | buf.WriteString(parent) 142 | if idx < len(v.Parts)-1 { 143 | buf.WriteRune(filepath.Separator) 144 | } 145 | 146 | } 147 | return buf.String() 148 | } 149 | -------------------------------------------------------------------------------- /src/semantic/reference.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "reflect" 5 | 6 | "github.com/ark-lang/ark/src/ast" 7 | ) 8 | 9 | type ReferenceCheck struct { 10 | InFunction int 11 | } 12 | 13 | func (_ ReferenceCheck) Name() string { return "reference" } 14 | 15 | func (v *ReferenceCheck) Init(s *SemanticAnalyzer) { 16 | v.InFunction = 0 17 | } 18 | 19 | func (v *ReferenceCheck) EnterScope(s *SemanticAnalyzer) {} 20 | func (v *ReferenceCheck) ExitScope(s *SemanticAnalyzer) {} 21 | 22 | func (v *ReferenceCheck) PostVisit(s *SemanticAnalyzer, n ast.Node) { 23 | switch n.(type) { 24 | case *ast.FunctionDecl, *ast.LambdaExpr: 25 | v.InFunction-- 26 | } 27 | } 28 | 29 | func (v *ReferenceCheck) Visit(s *SemanticAnalyzer, n ast.Node) { 30 | switch n.(type) { 31 | case *ast.FunctionDecl, *ast.LambdaExpr: 32 | v.InFunction++ 33 | } 34 | 35 | switch n := n.(type) { 36 | case *ast.FunctionDecl: 37 | v.checkFunction(s, n, n.Function) 38 | 39 | case *ast.LambdaExpr: 40 | v.checkFunction(s, n, n.Function) 41 | 42 | case *ast.VariableDecl: 43 | if v.InFunction <= 0 { 44 | if typeReferenceContainsReferenceType(n.Variable.Type, nil) { 45 | s.Err(n, "Global variable has reference-containing type `%s`", n.Variable.Type.String()) 46 | } 47 | } 48 | } 49 | } 50 | 51 | func (v *ReferenceCheck) checkFunction(s *SemanticAnalyzer, loc ast.Locatable, fn *ast.Function) { 52 | if typeReferenceContainsReferenceType(fn.Type.Return, nil) { 53 | s.Err(loc, "Function has reference-containing return type `%s`", fn.Type.Return.String()) 54 | } 55 | } 56 | 57 | func (v *ReferenceCheck) Finalize(s *SemanticAnalyzer) { 58 | 59 | } 60 | 61 | func typeReferenceContainsReferenceType(typ *ast.TypeReference, visited map[*ast.TypeReference]struct{ visited, value bool }) bool { 62 | if visited == nil { 63 | visited = make(map[*ast.TypeReference]struct{ visited, value bool }) 64 | } 65 | 66 | if visited[typ].visited { 67 | return visited[typ].value 68 | } 69 | visited[typ] = struct{ visited, value bool }{true, false} 70 | 71 | if typeContainsReferenceType(typ.BaseType, visited) { 72 | visited[typ] = struct{ visited, value bool }{true, true} 73 | return true 74 | } 75 | 76 | for _, garg := range typ.GenericArguments { 77 | if typeReferenceContainsReferenceType(garg, visited) { 78 | visited[typ] = struct{ visited, value bool }{true, true} 79 | return true 80 | } 81 | } 82 | 83 | return false 84 | } 85 | 86 | func typeContainsReferenceType(typ ast.Type, visited map[*ast.TypeReference]struct{ visited, value bool }) bool { 87 | switch typ := typ.ActualType().(type) { 88 | case ast.ReferenceType: 89 | return true 90 | 91 | case ast.PointerType: 92 | return typeReferenceContainsReferenceType(typ.Addressee, visited) 93 | 94 | case ast.ArrayType: 95 | return typeReferenceContainsReferenceType(typ.MemberType, visited) 96 | 97 | case ast.StructType: 98 | for _, field := range typ.Members { 99 | if typeReferenceContainsReferenceType(field.Type, visited) { 100 | return true 101 | } 102 | } 103 | return false 104 | 105 | case ast.EnumType: 106 | for _, member := range typ.Members { 107 | if typeContainsReferenceType(member.Type, visited) { 108 | return true 109 | } 110 | } 111 | return false 112 | 113 | case ast.TupleType: 114 | for _, field := range typ.Members { 115 | if typeReferenceContainsReferenceType(field, visited) { 116 | return true 117 | } 118 | } 119 | return false 120 | 121 | case *ast.SubstitutionType, ast.PrimitiveType, ast.FunctionType: 122 | return false 123 | 124 | default: 125 | panic("unimplemented type: " + reflect.TypeOf(typ).String()) 126 | } 127 | } 128 | -------------------------------------------------------------------------------- /lib/std/adt/stack.ark: -------------------------------------------------------------------------------- 1 | #use std::mem 2 | 3 | // TODO(felix): 4 | // - peek(offset) 5 | // - isEmpty() 6 | // - find(T) 7 | 8 | /// A simple LIFO (last in first out) queue/Stack. 9 | /// 10 | /// ## Example 11 | /// ``` 12 | /// #use std::adt 13 | /// 14 | /// pub func main() -> int { 15 | /// stack := adt::Stack::new(); 16 | /// return 0; 17 | /// } 18 | /// ``` 19 | pub type Stack struct { 20 | data: ^mut List, 21 | }; 22 | 23 | /// Create a new Stack 24 | /// 25 | /// ## Example 26 | /// ``` 27 | /// #use std::adt 28 | /// 29 | /// type Scope struct { 30 | /// parent: ^mut Scope, 31 | /// }; 32 | /// 33 | /// pub func main() -> int { 34 | /// scopes := adt::Stack::new(); 35 | /// defer scopes.destroy(); 36 | /// return 0; 37 | /// } 38 | /// ``` 39 | pub func (Stack) new() -> ^mut Stack { 40 | stack := mem::alloc>(); 41 | @stack = Stack { 42 | data: List::new(), 43 | }; 44 | return stack; 45 | } 46 | 47 | /// Returns the length of the stack, i.e. 48 | /// how many items there are in the stack. This 49 | /// is not necessarily how much memory has been 50 | /// allocated for the stack. 51 | /// 52 | /// ## Example 53 | /// ``` 54 | /// #use std::adt 55 | /// 56 | /// type Scope struct { 57 | /// parent: ^mut Scope, 58 | /// }; 59 | /// 60 | /// pub func main() -> int { 61 | /// scopes := adt::Stack::new(); 62 | /// defer scopes.destroy(); 63 | /// len := scopes.getLength(); 64 | /// return int(len); 65 | /// } 66 | /// ``` 67 | pub func (s: ^Stack) getLength() -> uint { 68 | return s.data.getLength(); 69 | } 70 | 71 | /// This returns how much memory has been 72 | /// allocated for the stack. 73 | /// 74 | /// ## Example 75 | /// ``` 76 | /// #use std::adt 77 | /// 78 | /// type Scope struct { 79 | /// parent: ^mut Scope, 80 | /// }; 81 | /// 82 | /// pub func main() -> int { 83 | /// scopes := adt::Stack::new(); 84 | /// defer scopes.destroy(); 85 | /// cap := scopes.getCapacity(); 86 | /// return int(cap); 87 | /// } 88 | /// ``` 89 | pub func (s: ^Stack) getCapacity() -> uint { 90 | return s.data.getCapacity(); 91 | } 92 | 93 | /// Pushes a value of type T to the 94 | /// stack. 95 | /// 96 | /// ## Example 97 | /// ``` 98 | /// #use std::adt 99 | /// 100 | /// type Scope struct { 101 | /// parent: ^mut Scope, 102 | /// }; 103 | /// 104 | /// pub func main() -> int { 105 | /// scopes := adt::Stack::new(); 106 | /// defer scopes.destroy(); 107 | /// mut scope: Scope; 108 | /// scopes.push(scope); 109 | /// return 0; 110 | /// } 111 | /// ``` 112 | pub func (s: ^Stack) push(val: T) { 113 | s.data.append(val); 114 | } 115 | 116 | /// Pops the top most value off the 117 | /// stack as an Option type. 118 | /// 119 | /// ## Example 120 | /// ``` 121 | /// #use std::adt 122 | /// 123 | /// type Scope struct { 124 | /// parent: ^mut Scope, 125 | /// }; 126 | /// 127 | /// pub func main() -> int { 128 | /// scopes := adt::Stack::new(); 129 | /// defer scopes.destroy(); 130 | /// mut scope: Scope; 131 | /// scopes.push(scope); 132 | /// myScope := scope.pop(); 133 | /// myScope.unwrap(); 134 | /// return 0; 135 | /// } 136 | /// ``` 137 | pub func (s: ^Stack) pop() -> Option { 138 | return s.data.pop(); 139 | } 140 | 141 | /// Destroys the Stack, clearing 142 | /// the stack of its contents 143 | /// 144 | /// ## Example 145 | /// ``` 146 | /// #use std::adt 147 | /// 148 | /// type Scope struct { 149 | /// parent: ^mut Scope, 150 | /// }; 151 | /// 152 | /// pub func main() -> int { 153 | /// scopes := adt::Stack::new(); 154 | /// defer scopes.destroy(); 155 | /// return 0; 156 | /// } 157 | /// ``` 158 | pub func (s: ^Stack) destroy() { 159 | s.data.destroy(); 160 | mem::free(s); 161 | } -------------------------------------------------------------------------------- /lib/std/unicode/utf8/coding.ark: -------------------------------------------------------------------------------- 1 | TRAILING_BYTES := []u8{ 2 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 3 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 4 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 5 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 6 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 7 | 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0, 8 | 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1,1,1,1,1,1,1,1,1, 9 | 2,2,2,2,2,2,2,2,2,2,2,2,2,2,2,2, 3,3,3,3,3,3,3,3,4,4,4,4,5,5,5,5 10 | }; 11 | 12 | OFFSETS := []u32{ 13 | 0x00000000, 0x00003080, 0x000E2080, 14 | 0x03C82080, 0xFA082080, 0x82082080 15 | }; 16 | 17 | pub RUNE_MAX_BYTES := 4; 18 | 19 | pub func numRunes(src: []u8) -> uint { 20 | mut i: uint = 0; 21 | mut num: uint = 0; 22 | 23 | for i < len(src) { 24 | nb := uint(TRAILING_BYTES[src[i]]); 25 | i += nb + 1; 26 | num += 1; 27 | } 28 | 29 | return num; 30 | } 31 | 32 | pub func numBytes(src: []rune) -> uint { 33 | mut i: uint = 0; 34 | mut num: uint = 0; 35 | 36 | for i < len(src) { 37 | ch := src[i]; 38 | if ch < 0x80 { 39 | num += 1; 40 | } else if ch < 0x800 { 41 | num += 2; 42 | } else if ch < 0x10000 { 43 | num += 3; 44 | } else if ch < 0x110000 { 45 | num += 4; 46 | } 47 | i += 1; 48 | } 49 | return num; 50 | } 51 | 52 | pub func decode(mut dest: []rune, src: []u8) -> uint { 53 | mut srcIndex: uint = 0; 54 | mut destIndex: uint = 0; 55 | 56 | for destIndex < len(dest) { 57 | nb := uint(TRAILING_BYTES[src[srcIndex]]); 58 | if srcIndex + nb >= len(src) { 59 | // Src to short, so we can't read more 60 | return destIndex; 61 | } 62 | 63 | mut ch: rune = 0; 64 | if nb >= 3 { 65 | ch += rune(src[srcIndex]); ch <<= 6; 66 | srcIndex += 1; 67 | } 68 | if nb >= 2 { 69 | ch += rune(src[srcIndex]); ch <<= 6; 70 | srcIndex += 1; 71 | } 72 | if nb >= 1 { 73 | ch += rune(src[srcIndex]); ch <<= 6; 74 | srcIndex += 1; 75 | } 76 | ch += rune(src[srcIndex]); 77 | srcIndex += 1; 78 | 79 | ch -= rune(OFFSETS[nb]); 80 | dest[destIndex] = ch; 81 | destIndex += 1; 82 | } 83 | 84 | return destIndex; 85 | } 86 | 87 | pub func encode(mut dest: []u8, src: []rune) -> uint { 88 | mut srcIndex: uint = 0; 89 | mut destIndex: uint = 0; 90 | for srcIndex < len(src) { 91 | ch := src[srcIndex]; 92 | if ch < 0x80 { 93 | if destIndex >= len(dest) { 94 | return destIndex; 95 | } 96 | dest[destIndex] = u8(ch); 97 | destIndex += 1; 98 | } 99 | else if ch < 0x800 { 100 | if destIndex >= len(dest)-1 { 101 | return destIndex; 102 | } 103 | dest[destIndex + 0] = u8((ch>>6) | 0xC0); 104 | dest[destIndex + 1] = u8((ch & 0x3F) | 0x80); 105 | destIndex += 2; 106 | } 107 | else if ch < 0x10000 { 108 | if destIndex >= len(dest)-2 { 109 | return destIndex; 110 | } 111 | dest[destIndex + 0] = u8((ch>>12) | 0xE0); 112 | dest[destIndex + 1] = u8(((ch>>6) & 0x3F) | 0x80); 113 | dest[destIndex + 2] = u8((ch & 0x3F) | 0x80); 114 | destIndex += 3; 115 | } 116 | else if ch < 0x110000 { 117 | if destIndex >= len(dest)-3 { 118 | return destIndex; 119 | } 120 | dest[destIndex + 0] = u8((ch>>18) | 0xF0); 121 | dest[destIndex + 1] = u8(((ch>>12) & 0x3F) | 0x80); 122 | dest[destIndex + 2] = u8(((ch>>6) & 0x3F) | 0x80); 123 | dest[destIndex + 3] = u8((ch & 0x3F) | 0x80); 124 | destIndex += 4; 125 | } 126 | srcIndex += 1; 127 | } 128 | 129 | return destIndex; 130 | } 131 | -------------------------------------------------------------------------------- /src/semantic/semantic.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | 7 | "github.com/ark-lang/ark/src/ast" 8 | "github.com/ark-lang/ark/src/util" 9 | "github.com/ark-lang/ark/src/util/log" 10 | ) 11 | 12 | type SemanticAnalyzer struct { 13 | Module *ast.Module 14 | Submodule *ast.Submodule 15 | unresolvedNodes []*ast.Node 16 | shouldExit bool 17 | 18 | Check SemanticCheck 19 | } 20 | 21 | type SemanticCheck interface { 22 | Init(s *SemanticAnalyzer) 23 | EnterScope(s *SemanticAnalyzer) 24 | ExitScope(s *SemanticAnalyzer) 25 | Visit(*SemanticAnalyzer, ast.Node) 26 | PostVisit(*SemanticAnalyzer, ast.Node) 27 | Finalize(*SemanticAnalyzer) 28 | Name() string 29 | } 30 | 31 | func (v *SemanticAnalyzer) Err(thing ast.Locatable, err string, stuff ...interface{}) { 32 | pos := thing.Pos() 33 | 34 | log.Error("semantic", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" [%s:%d:%d] %s\n", 35 | pos.Filename, pos.Line, pos.Char, fmt.Sprintf(err, stuff...)) 36 | 37 | log.Errorln("semantic", v.Submodule.File.MarkPos(pos)) 38 | 39 | v.shouldExit = true 40 | } 41 | 42 | func (v *SemanticAnalyzer) Warn(thing ast.Locatable, err string, stuff ...interface{}) { 43 | pos := thing.Pos() 44 | 45 | log.Warning("semantic", util.TEXT_YELLOW+util.TEXT_BOLD+"warning:"+util.TEXT_RESET+" [%s:%d:%d] %s\n", 46 | pos.Filename, pos.Line, pos.Char, fmt.Sprintf(err, stuff...)) 47 | 48 | log.Warningln("semantic", v.Submodule.File.MarkPos(pos)) 49 | } 50 | 51 | func SemCheck(module *ast.Module, ignoreUnused bool) { 52 | checks := []SemanticCheck{ 53 | &AttributeCheck{}, 54 | &UnreachableCheck{}, 55 | &BreakAndNextCheck{}, 56 | &DeprecatedCheck{}, 57 | &RecursiveDefinitionCheck{}, 58 | &TypeCheck{}, 59 | &ImmutableAssignCheck{}, 60 | &UseBeforeDeclareCheck{}, 61 | &MiscCheck{}, 62 | &ReferenceCheck{}, 63 | } 64 | 65 | if !ignoreUnused { 66 | checks = append(checks, &UnusedCheck{}) 67 | } 68 | 69 | for _, check := range checks { 70 | log.Timed("analysis pass", check.Name(), func() { 71 | for _, submod := range module.Parts { 72 | log.Timed("checking submodule", module.Name.String()+"/"+submod.File.Name, func() { 73 | res := &SemanticAnalyzer{ 74 | Module: module, 75 | Submodule: submod, 76 | Check: check, 77 | } 78 | res.Init() 79 | 80 | vis := ast.NewASTVisitor(res) 81 | vis.VisitSubmodule(submod) 82 | 83 | res.Finalize() 84 | }) 85 | 86 | } 87 | }) 88 | } 89 | } 90 | 91 | // the initial check for a semantic pass 92 | // this will be called _once_ and should be 93 | // used to initialize things, etc... 94 | func (v *SemanticAnalyzer) Init() { 95 | v.Check.Init(v) 96 | } 97 | 98 | // Finalize is called after all checks have been run, and should be used for 99 | // cleaning up and any checks that depend on having completely traversed the 100 | // syntax tree. 101 | func (v *SemanticAnalyzer) Finalize() { 102 | // If we already encountered an error, exit now 103 | if v.shouldExit { 104 | os.Exit(util.EXIT_FAILURE_SEMANTIC) 105 | } 106 | 107 | // destroy stuff before finalisation 108 | v.Check.Finalize(v) 109 | 110 | if v.shouldExit { 111 | os.Exit(util.EXIT_FAILURE_SEMANTIC) 112 | } 113 | } 114 | 115 | func (v *SemanticAnalyzer) Visit(n *ast.Node) bool { 116 | v.Check.Visit(v, *n) 117 | 118 | // NOTE: The following means that if we encountered an error we will not 119 | // analyze further down the AST. This should hinder some panics with 120 | // relation to invalid data. 121 | return !v.shouldExit 122 | } 123 | 124 | func (v *SemanticAnalyzer) PostVisit(n *ast.Node) { 125 | v.Check.PostVisit(v, *n) 126 | } 127 | 128 | func (v *SemanticAnalyzer) EnterScope() { 129 | v.Check.EnterScope(v) 130 | } 131 | 132 | func (v *SemanticAnalyzer) ExitScope() { 133 | v.Check.ExitScope(v) 134 | } 135 | -------------------------------------------------------------------------------- /src/semantic/attributes.go: -------------------------------------------------------------------------------- 1 | package semantic 2 | 3 | import ( 4 | "github.com/ark-lang/ark/src/ast" 5 | "github.com/ark-lang/ark/src/parser" 6 | ) 7 | 8 | type AttributeCheck struct { 9 | } 10 | 11 | func (v *AttributeCheck) Init(s *SemanticAnalyzer) {} 12 | func (v *AttributeCheck) EnterScope(s *SemanticAnalyzer) {} 13 | func (v *AttributeCheck) ExitScope(s *SemanticAnalyzer) {} 14 | 15 | func (v *AttributeCheck) PostVisit(s *SemanticAnalyzer, n ast.Node) {} 16 | 17 | func (_ AttributeCheck) Name() string { return "attribute" } 18 | 19 | func (v *AttributeCheck) Visit(s *SemanticAnalyzer, n ast.Node) { 20 | switch n := n.(type) { 21 | case *ast.TypeDecl: 22 | typ := n.NamedType.Type 23 | switch typ.(type) { 24 | case ast.StructType: 25 | v.CheckStructType(s, typ.(ast.StructType)) 26 | } 27 | 28 | case *ast.FunctionDecl: 29 | v.CheckFunctionDecl(s, n) 30 | //case *ast.TraitDecl: 31 | // v.CheckTraitDecl(s, n) 32 | 33 | case *ast.VariableDecl: 34 | v.CheckVariableDecl(s, n) 35 | } 36 | } 37 | 38 | func (v *AttributeCheck) Finalize(s *SemanticAnalyzer) { 39 | 40 | } 41 | 42 | func (v *AttributeCheck) CheckFunctionDecl(s *SemanticAnalyzer, n *ast.FunctionDecl) { 43 | v.CheckAttrsDistanceFromLine(s, n.Function.Type.Attrs(), n.Pos().Line, "function", n.Function.Name) 44 | 45 | for _, attr := range n.Function.Type.Attrs() { 46 | switch attr.Key { 47 | case "deprecated": 48 | case "c": 49 | case "call_conv": 50 | case "nomangle": 51 | case "inline": 52 | switch attr.Value { 53 | case "always": 54 | case "never": 55 | case "maybe": 56 | default: 57 | s.Err(attr, "Invalid value `%s` for [inline] attribute", attr.Value) 58 | } 59 | default: 60 | s.Err(attr, "Invalid function attribute key `%s`", attr.Key) 61 | } 62 | } 63 | } 64 | 65 | func (v *AttributeCheck) CheckStructType(s *SemanticAnalyzer, n ast.StructType) { 66 | for _, attr := range n.Attrs() { 67 | switch attr.Key { 68 | case "packed": 69 | if attr.Value != "" { 70 | s.Err(attr, "Struct attribute `%s` doesn't expect value", attr.Key) 71 | } 72 | case "deprecated": 73 | // value is optional, nothing to check 74 | default: 75 | s.Err(attr, "Invalid struct attribute key `%s`", attr.Key) 76 | } 77 | } 78 | } 79 | 80 | /*func (v *AttributeCheck) CheckTraitDecl(s *SemanticAnalyzer, n *ast.TraitDecl) { 81 | v.CheckAttrsDistanceFromLine(s, n.Trait.Attrs(), n.Pos().Line, "type", n.Trait.TypeName()) 82 | 83 | for _, attr := range n.Trait.Attrs() { 84 | if attr.Key != "deprecated" { 85 | s.Err(attr, "Invalid trait attribute key `%s`", attr.Key) 86 | } 87 | } 88 | }*/ 89 | 90 | func (v *AttributeCheck) CheckVariableDecl(s *SemanticAnalyzer, n *ast.VariableDecl) { 91 | v.CheckAttrsDistanceFromLine(s, n.Variable.Attrs, n.Pos().Line, "variable", n.Variable.Name) 92 | 93 | for _, attr := range n.Variable.Attrs { 94 | switch attr.Key { 95 | case "deprecated": 96 | // value is optional, nothing to check 97 | case "nozero": 98 | default: 99 | s.Err(attr, "Invalid variable attribute key `%s`", attr.Key) 100 | } 101 | } 102 | } 103 | 104 | func (v *AttributeCheck) CheckAttrsDistanceFromLine(s *SemanticAnalyzer, attrs parser.AttrGroup, line int, declType, declName string) { 105 | // Turn map into a list sorted by line number 106 | var sorted []*parser.Attr 107 | for _, attr := range attrs { 108 | index := 0 109 | for idx, innerAttr := range sorted { 110 | if attr.Pos().Line >= innerAttr.Pos().Line { 111 | index = idx 112 | } 113 | } 114 | 115 | sorted = append(sorted, nil) 116 | copy(sorted[index+1:], sorted[index:]) 117 | sorted[index] = attr 118 | } 119 | 120 | for i := len(sorted) - 1; i >= 0; i-- { 121 | if sorted[i].Pos().Line < line-1 { 122 | // mute warnings from attribute blocks 123 | if !sorted[i].FromBlock { 124 | s.Warn(sorted[i], "Gap of %d lines between declaration of %s `%s` and `%s` attribute", line-sorted[i].Pos().Line, declType, declName, sorted[i].Key) 125 | } 126 | } 127 | line = sorted[i].Pos().Line 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /src/doc/file.go: -------------------------------------------------------------------------------- 1 | package doc 2 | 3 | import ( 4 | "html/template" 5 | "os" 6 | "path/filepath" 7 | "strings" 8 | ) 9 | 10 | var fileTemplate = template.Must(template.New("file").Parse(FILE_TEMPLATE_STR)) 11 | 12 | type File struct { 13 | Name string 14 | RootLoc string // path from this file to the root directory (the directory containing index.html) 15 | VariableDecls []*Decl 16 | StructDecls []*Decl 17 | TraitDecls []*Decl 18 | ImplDecls []*Decl 19 | FunctionDecls []*Decl 20 | } 21 | 22 | type FileTempData struct { 23 | Name string 24 | Decls []*Decl 25 | } 26 | 27 | func (v *File) dir() string { 28 | return "files/" + filepath.Dir(v.Name) + "/" 29 | } 30 | 31 | func (v *File) base() string { 32 | return filepath.Base(v.Name) 33 | } 34 | 35 | func (v *Docgen) generateFile(file *File) { 36 | fileDir := v.Dir + file.dir() 37 | err := os.MkdirAll(fileDir, os.ModeDir|0777) 38 | if err != nil { 39 | panic(err) 40 | } 41 | 42 | out, err := os.OpenFile(fileDir+file.base()+".html", os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0666) 43 | if err != nil { 44 | panic(err) 45 | } 46 | defer out.Close() 47 | 48 | for i := 0; i < 1+strings.Count(filepath.Clean(file.Name), "/"); i++ { 49 | file.RootLoc += "../" 50 | } 51 | 52 | err = fileTemplate.Execute(out, file) 53 | if err != nil { 54 | panic(err) 55 | } 56 | } 57 | 58 | const FILE_TEMPLATE_STR = ` 59 | 60 | 61 | 62 | {{.Name}} 63 | 64 | 65 | 66 | 67 | 68 | 69 |
70 |
71 |

File {{.Name}}

72 | Index 73 |
74 |
75 | 76 |
77 |
78 |

Overview

79 | 86 |
87 | 88 |
89 |

Variables

90 | {{range .VariableDecls}} 91 |

{{.Ident}}

92 |
{{.Snippet}}
93 |
{{.ParsedDocs}}
94 | {{end}} 95 |
96 | 97 |
98 |

Structs

99 | {{range .StructDecls}} 100 |

{{.Ident}}

101 |
{{.Snippet}}
102 |
{{.ParsedDocs}}
103 | {{end}} 104 |
105 | 106 |
107 |

Traits

108 | {{range .TraitDecls}} 109 |

{{.Ident}}

110 |
{{.Snippet}}
111 |
{{.ParsedDocs}}
112 | {{end}} 113 |
114 | 115 |
116 |

Implementations

117 | {{range .ImplDecls}} 118 |

{{.Ident}}

119 |
{{.Snippet}}
120 |
{{.ParsedDocs}}
121 | {{end}} 122 |
123 | 124 |
125 |

Functions

126 | {{range .FunctionDecls}} 127 |

{{.Ident}}

128 |
{{.Snippet}}
129 |
{{.ParsedDocs}}
130 | {{end}} 131 |
132 |
133 | 134 | ` 135 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Ark [![Build Status](https://api.travis-ci.org/ark-lang/ark.svg?branch=master)][1] [![license](http://img.shields.io/badge/license-MIT-brightgreen.svg)](https://raw.githubusercontent.com/ark-lang/ark/master/LICENSE) 2 | [1]: https://travis-ci.org/ark-lang/ark "Build Status" 3 | 4 | 5 | 6 | [Ark](//www.ark-lang.org) is a systems programming language focused on being practical and pragmatic. We want a language that is simple, easy to write, yet powerful enough to create readable, performant and concise code for any problem 7 | 8 | On the right is a gif of an example program [ark-gl](//github.com/ark-lang/ark-gl) written in Ark using OpenGL and GLFW. 9 | 10 | ## Index 11 | * [Getting Involved](#getting-involved) 12 | * [Example](#example) 13 | * [Installing](#installing) 14 | * [Dependencies](#dependencies) 15 | * [Building](#building) 16 | * [Compiling Ark code](#compiling-ark-code) 17 | 18 | ## Getting Involved 19 | Check out the [contributing guide](/CONTRIBUTING.md), there's a lot of information there to give you ideas of how you can help out. 20 | 21 | ## Example 22 | Ark is still a work in progress, this code sample reflects what Ark can 23 | do *currently*, though the way you write the following will likely change 24 | in the near future. 25 | 26 | More examples can be found [here](/examples). 27 | 28 | ```rust 29 | // binding to printf 30 | [c] func printf(fmt: ^u8, ...); 31 | 32 | pub func main(argc: int, argv: ^^u8) -> int { 33 | // accessed via the C module 34 | C::printf(c"Running %s\n", ^argv); 35 | 36 | // mutable i, type inferred 37 | mut i := 0; 38 | 39 | for i < 5 { 40 | C::printf(c"%d\n", i); 41 | 42 | i += 1; 43 | } 44 | return 0; 45 | } 46 | ``` 47 | 48 | ## Installing 49 | Installing Ark is simple, you'll need a few dependencies 50 | before you get started: 51 | 52 | ### Dependencies 53 | * Go installed and `$GOPATH` setup - [Instructions on setting up GOPATH](//golang.org/doc/code.html#GOPATH) 54 | * For building LLVM bindings: 55 | * Subversion 56 | * A C++ Compiler 57 | * CMake installed 58 | * `libedit-dev` installed 59 | 60 | ### Building 61 | Once you have your dependencies setup, building ark from scratch is done by 62 | running the following commands: 63 | 64 | ```bash 65 | $ git clone https://github.com/ark-lang/go-llvm.git $GOPATH/src/github.com/ark-lang/go-llvm 66 | $ cd $GOPATH/src/github.com/ark-lang/go-llvm 67 | $ ./build.sh 68 | $ go get github.com/ark-lang/ark/... 69 | ``` 70 | 71 | The `ark` binary will be built in `$GOPATH/bin`. To use the compiler, 72 | make sure `$GOPATH/bin` is in your `$PATH`. 73 | 74 | ### Compiling Ark code 75 | Currently the module system Ark uses is a work in progress. As of writing this, 76 | each ark file represents a module. A module has a child-module "C" which 77 | contains all of the C functions and other bindings you may write. 78 | 79 | Given the following project structure: 80 | 81 | src/ 82 | - entities/ 83 | - entity.ark 84 | - player.ark 85 | - main.ark 86 | 87 | To compile this, you would pass through the file which contains the main 88 | entry point (main function) to your program, which is conventionally named "main.ark". 89 | 90 | Since our main file is in another folder, we need to set the src folder as 91 | an include directory so that the compiler doesn't think it's a module. We use 92 | the `-I` flag for this: 93 | 94 | ark build -I src src/main.ark --loglevel=debug 95 | 96 | This should compile your code, and produce an executable called "main", which 97 | you can then run. 98 | 99 | For more information on the module system and how it works, 100 | refer to the ["Modules and Dependencies"](http://book.ark-lang.org/modules.html) 101 | section in the Ark reference. 102 | 103 | For more information on program flags, refer to the 104 | ["Program Input"](http://book.ark-lang.org/source.html), section in the Ark 105 | reference. 106 | -------------------------------------------------------------------------------- /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to Ark 2 | First of all, thanks for your interest in contributing to Ark! This 3 | document is outlines ways in which you can contribute to Ark, and also 4 | some helpful things to know if you are working on the compiler. 5 | 6 | ## Index 7 | * [Other Links](#other-links) 8 | * [Communication](#communication) 9 | * [IRC](#irc) 10 | * [Reddit](#reddit) 11 | * [Direct Contribution](#direct-contribution) 12 | * [GitHub Issues](#github-issues) 13 | * [Issue Labels](#issue-labels) 14 | * [Pull Requests](#pull-requests) 15 | * [Utilities](#utilities) 16 | * [`make gen` and `make fmt`](#make-gen-and-make-fmt) 17 | * [Testing](#testing) 18 | * [Writing Tests](#writing-tests) 19 | 20 | ## Other Links 21 | * [/r/ark_lang](//reddit.com/r/ark_lang) 22 | * [ark documentation](//book.ark-lang.org) 23 | * [ark website](//ark-lang.org) 24 | 25 | ## Communication 26 | ### IRC 27 | The preferred method of communication between developers is 28 | the IRC, this is on the `irc.freenode.net` server, and our 29 | channel is `#ark-lang`. 30 | 31 | ### Reddit 32 | We also have a sub-reddit for discussion, help, and more. A lot 33 | of the developers are Reddit addicts, so we can help you out 34 | with any questions. We usually post occasional updates to the 35 | subreddit. 36 | 37 | ## Direct Contribution 38 | ### GitHub Issues 39 | This is a more formal method of communcation, any bugs, errors, 40 | or discussion that should be documented in some way goes here. 41 | 42 | #### Issue Labels 43 | To keep things organized, we have introduced various labels that contributors with 44 | sufficient permissions can add to issues. **This also means that anyone 45 | who is new and looking for issues related to a specific field, or complexity 46 | can easily filter the issues to their advantage!**. 47 | 48 | Labels are prefixed with a letter, the letter denotes the category of label: 49 | 50 | * E: A label prefixed with E is how much experience is required to take on the issue 51 | * M: These are miscellaneous issues, typically ones too broad to categorize 52 | * O: These are the operating system/platform that an issue is relevant to 53 | * P: The priority of the issue 54 | * S: The stage in which the issue occurs (note not all issues have stages) 55 | * A: An area in which the issue is relevant to, e.g. A-tests is for issues related to tests 56 | 57 | ### Pull Requests 58 | We love Pull Requests! To keep everything in working order, make 59 | sure you follow these guidelines: 60 | 61 | * Run `make fmt` on your code before you submit a PR 62 | * Work on a separate branch, named appropriately e.g. `for-loop-codegen-fix` 63 | * If your patch is not up to date with the master branch, please rebase 64 | * If you are implementing a feature, make sure there is a text if there is not an existing one already 65 | * Please add a summary/description of your pull request, the more detail the better. 66 | 67 | Once your PR has been submitted, one of the developers will review your code 68 | and merge it if everything is ok! 69 | 70 | ## Utilities 71 | ### `make gen` and `make fmt` 72 | The targets `gen` and `fmt` are included for the convenience of the developers. 73 | They run `go generate` and `go fmt` respectively on all the modules in Ark. 74 | Please run `make fmt` before creating a pull request. 75 | 76 | ### Testing 77 | Requires `$GOPATH/bin` to be in your `$PATH` and Python 2.4 or newer. 78 | 79 | ### Writing Tests 80 | Writing tests are very important. The tests are located 81 | in the `tests/` directory in this repository. Each test has 82 | an ark module, and its corresponding TOML file. 83 | 84 | Note that there is a tool for creating template test files in 85 | the util directory: 86 | 87 | ./tools/make-test.sh tests/the-program.ark 88 | 89 | In the TOML test file, you specify various arguments such as 90 | what sourcefile to target, the compiler arguments to pass, and 91 | so on. 92 | 93 | Below is an example test file: 94 | 95 | Name = "my_test" 96 | Sourcefile = "my_test.ark" 97 | 98 | CompilerArgs = [] 99 | RunArgs = [] 100 | 101 | CompilerError = 0 102 | RunError = 0 103 | 104 | Input = "" 105 | 106 | CompilerOutput = "" 107 | RunOutput = "" 108 | 109 | A test file should have a name that is meaningful, and shows 110 | the developer at first glance, what is being tested. 111 | 112 | You can run these tests by executing the `ark-testrunner` command 113 | in your terminal (assuming `$GOPATH/bin` is in your `$PATH`). -------------------------------------------------------------------------------- /src/ast/mangle.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "bytes" 5 | "fmt" 6 | ) 7 | 8 | // In case we support multiple name mangling schemes 9 | type MangleType int 10 | 11 | const ( 12 | MANGLE_ARK_UNSTABLE MangleType = iota 13 | ) 14 | 15 | // TODO GenericInstance -> GenericContext 16 | 17 | func TypeReferencesMangledName(mangleType MangleType, typs []*TypeReference, gcon *GenericContext) string { 18 | res := "" 19 | for _, typ := range typs { 20 | res += TypeReferenceMangledName(mangleType, typ, gcon) 21 | } 22 | return res 23 | } 24 | 25 | // easier than making a method for all types 26 | func TypeReferenceMangledName(mangleType MangleType, typ *TypeReference, gcon *GenericContext) string { 27 | switch mangleType { 28 | case MANGLE_ARK_UNSTABLE: 29 | res := "_" 30 | 31 | for { 32 | if ptr, ok := typ.BaseType.(PointerType); ok { 33 | typ = ptr.Addressee 34 | res += "p" 35 | } else { 36 | break 37 | } 38 | } 39 | 40 | switch typ := typ.BaseType.(type) { 41 | case ArrayType: 42 | res += fmt.Sprintf("A%s", TypeReferenceMangledName(mangleType, typ.MemberType, gcon)) 43 | 44 | case ReferenceType: 45 | var suffix string 46 | if typ.IsMutable { 47 | suffix = "M" 48 | } else { 49 | suffix = "C" 50 | } 51 | res += fmt.Sprintf("R%s%s", suffix, TypeReferenceMangledName(mangleType, typ.Referrer, gcon)) 52 | 53 | case EnumType: 54 | res += fmt.Sprintf("E%d", len(typ.Members)) 55 | for _, mem := range typ.Members { 56 | res += TypeReferenceMangledName(mangleType, &TypeReference{BaseType: mem.Type}, gcon) 57 | } 58 | 59 | case StructType: 60 | res += fmt.Sprintf("S%d", len(typ.Members)) 61 | for _, mem := range typ.Members { 62 | res += TypeReferenceMangledName(mangleType, mem.Type, gcon) 63 | } 64 | 65 | case TupleType: 66 | res += fmt.Sprintf("T%d", len(typ.Members)) 67 | for _, mem := range typ.Members { 68 | res += TypeReferenceMangledName(mangleType, mem, gcon) 69 | } 70 | 71 | case FunctionType: 72 | str := TypeReferencesMangledName(mangleType, typ.Parameters, gcon) 73 | 74 | str += TypeReferenceMangledName(mangleType, typ.Return, gcon) 75 | 76 | if typ.Receiver != nil { 77 | str = TypeReferenceMangledName(mangleType, typ.Receiver, gcon) + str 78 | } 79 | 80 | res += fmt.Sprintf("%dFT%s", len(str), str) 81 | 82 | case *NamedType, PrimitiveType: 83 | name := typ.TypeName() 84 | res += fmt.Sprintf("%d%s", len(name), name) 85 | 86 | case InterfaceType: 87 | str := "" 88 | for _, fn := range typ.Functions { 89 | str += fn.MangledName(mangleType, gcon) 90 | } 91 | 92 | res += fmt.Sprintf("%dI%s", len(str), str) 93 | 94 | case *SubstitutionType: 95 | if sub := gcon.GetSubstitutionType(typ); sub != nil { 96 | it := gcon.Get(&TypeReference{BaseType: typ}) 97 | if it.BaseType == typ { 98 | panic("INTERNAL ERROR: Substitution type mapped to itself") 99 | } 100 | res = TypeReferenceMangledName(mangleType, it, gcon) 101 | } else { 102 | res = typ.Name 103 | } 104 | 105 | default: 106 | panic("unimplemented type mangling scheme") 107 | 108 | } 109 | 110 | gas := TypeReferencesMangledName(mangleType, typ.GenericArguments, gcon) 111 | if len(gas) > 0 { 112 | res += "GA" + gas 113 | } 114 | 115 | return res 116 | default: 117 | panic("") 118 | } 119 | } 120 | 121 | func (v Module) MangledName(typ MangleType) string { 122 | switch typ { 123 | case MANGLE_ARK_UNSTABLE: 124 | buf := new(bytes.Buffer) 125 | for _, mod := range v.Name.Parts { 126 | buf.WriteString("_M") 127 | buf.WriteString(fmt.Sprintf("%d", len(mod))) 128 | buf.WriteString(mod) 129 | } 130 | 131 | return buf.String() 132 | default: 133 | panic("") 134 | } 135 | } 136 | 137 | func (v Function) MangledName(typ MangleType, gcon *GenericContext) string { 138 | if v.Name == "main" { 139 | return "main" // TODO make sure only one main function 140 | } 141 | 142 | switch typ { 143 | case MANGLE_ARK_UNSTABLE: 144 | var prefix string 145 | if v.Type.Receiver != nil { 146 | prefix = "m" 147 | } else if v.StaticReceiverType != nil { 148 | prefix = "s" 149 | } 150 | 151 | result := fmt.Sprintf("_%sF%d%s", prefix, len(v.Name), v.Name) 152 | for _, arg := range v.Parameters { 153 | result += TypeReferenceMangledName(typ, arg.Variable.Type, gcon) 154 | } 155 | 156 | result += TypeReferenceMangledName(typ, v.Type.Return, gcon) 157 | 158 | if v.Type.Receiver != nil { 159 | result = TypeReferenceMangledName(typ, v.Type.Receiver, gcon) + result 160 | } else if v.StaticReceiverType != nil { 161 | result = TypeReferenceMangledName(typ, &TypeReference{BaseType: v.StaticReceiverType}, gcon) + result 162 | } 163 | 164 | result = v.ParentModule.MangledName(typ) + result 165 | 166 | return result 167 | default: 168 | panic("") 169 | } 170 | } 171 | 172 | func (v Variable) MangledName(typ MangleType) string { 173 | switch typ { 174 | case MANGLE_ARK_UNSTABLE: 175 | result := fmt.Sprintf("_V%d%s", len(v.Name), v.Name) 176 | return result 177 | default: 178 | panic("") 179 | } 180 | } 181 | -------------------------------------------------------------------------------- /src/ast/scope.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | "strings" 7 | 8 | "github.com/ark-lang/ark/src/util/log" 9 | 10 | "github.com/ark-lang/ark/src/util" 11 | ) 12 | 13 | type IdentType int 14 | 15 | const ( 16 | IDENT_VARIABLE IdentType = iota 17 | IDENT_TYPE 18 | IDENT_FUNCTION 19 | IDENT_MODULE 20 | ) 21 | 22 | func (v IdentType) String() string { 23 | switch v { 24 | case IDENT_FUNCTION: 25 | return "function" 26 | case IDENT_MODULE: 27 | return "module" 28 | case IDENT_TYPE: 29 | return "type" 30 | case IDENT_VARIABLE: 31 | return "variable" 32 | default: 33 | panic("unimplemented ident type") 34 | } 35 | } 36 | 37 | type Ident struct { 38 | Type IdentType 39 | Value interface{} 40 | Public bool 41 | Scope *Scope 42 | } 43 | 44 | type Scope struct { 45 | Outer *Scope 46 | Idents map[string]*Ident 47 | Module *Module // module this scope belongs to, nil if builtin 48 | Function *Function // function this scope is inside, nil if global/builtin/etc 49 | UsedModules map[string]*Module 50 | } 51 | 52 | func newScope(outer *Scope, mod *Module, fn *Function) *Scope { 53 | return &Scope{ 54 | Outer: outer, 55 | Idents: make(map[string]*Ident), 56 | UsedModules: make(map[string]*Module), 57 | Module: mod, 58 | Function: fn, 59 | } 60 | } 61 | 62 | var builtinScope *Scope 63 | 64 | var runeType, stringType Type 65 | 66 | func init() { 67 | builtinScope = newScope(nil, nil, nil) 68 | 69 | for i := 0; i < len(_PrimitiveType_index); i++ { 70 | builtinScope.InsertType(PrimitiveType(i), true) 71 | } 72 | 73 | stringType = &NamedType{Type: ArrayOf(&TypeReference{BaseType: PRIMITIVE_u8}, false, 0), Name: "string"} 74 | runeType = &NamedType{Type: PRIMITIVE_u32, Name: "rune"} 75 | 76 | builtinScope.InsertType(stringType, true) 77 | builtinScope.InsertType(runeType, true) 78 | } 79 | 80 | func NewGlobalScope(mod *Module) *Scope { 81 | s := newScope(builtinScope, mod, nil) 82 | 83 | return s 84 | } 85 | 86 | func NewCScope(mod *Module) *Scope { 87 | s := newScope(nil, mod, nil) 88 | s.InsertType(&NamedType{Name: "uint", Type: PRIMITIVE_u32}, true) 89 | s.InsertType(&NamedType{Name: "int", Type: PRIMITIVE_s32}, true) 90 | s.InsertType(&NamedType{Name: "void", Type: PRIMITIVE_u8}, true) 91 | return s 92 | } 93 | 94 | func (v *Scope) err(err string, stuff ...interface{}) { 95 | // TODO: These errors are unacceptably shitty 96 | log.Error("resolve", util.TEXT_RED+util.TEXT_BOLD+"error:"+util.TEXT_RESET+" %s\n", 97 | fmt.Sprintf(err, stuff...)) 98 | os.Exit(util.EXIT_FAILURE_PARSE) 99 | } 100 | 101 | func (v *Scope) InsertIdent(value interface{}, name string, typ IdentType, public bool) *Ident { 102 | c := v.Idents[name] 103 | if c == nil { 104 | v.Idents[name] = &Ident{ 105 | Type: typ, 106 | Value: value, 107 | Public: public, 108 | Scope: v, 109 | } 110 | } 111 | return c 112 | } 113 | 114 | func (v *Scope) InsertType(t Type, public bool) *Ident { 115 | if sub, ok := t.(*SubstitutionType); ok { 116 | return v.InsertIdent(t, sub.Name, IDENT_TYPE, public) 117 | } 118 | return v.InsertIdent(t, t.TypeName(), IDENT_TYPE, public) 119 | } 120 | 121 | func (v *Scope) InsertVariable(t *Variable, public bool) *Ident { 122 | return v.InsertIdent(t, t.Name, IDENT_VARIABLE, public) 123 | } 124 | 125 | func (v *Scope) InsertFunction(t *Function, public bool) *Ident { 126 | return v.InsertIdent(t, t.Name, IDENT_FUNCTION, public) 127 | } 128 | 129 | func (v *Scope) UseModule(t *Module) { 130 | v.UsedModules[t.Name.Last()] = t 131 | } 132 | 133 | func (v *Scope) UseModuleAs(t *Module, name string) { 134 | v.UsedModules[name] = t 135 | } 136 | 137 | func (v *Scope) GetIdent(name UnresolvedName) *Ident { 138 | scope := v 139 | 140 | for idx, modname := range name.ModuleNames { 141 | module, ok := scope.UsedModules[modname] 142 | for !ok && scope.Outer != nil { 143 | scope = scope.Outer 144 | module, ok = scope.UsedModules[modname] 145 | } 146 | 147 | if !ok { 148 | // Check for the case of the static method 149 | if idx == len(name.ModuleNames)-1 { 150 | lastName, method := name.Split() 151 | if r := v.GetIdent(lastName); r != nil && r.Type == IDENT_TYPE { 152 | typ := r.Value.(Type) 153 | if nt, ok := typ.(*NamedType); ok { 154 | fn := nt.GetStaticMethod(method) 155 | if fn != nil { 156 | return &Ident{IDENT_FUNCTION, fn, true, scope} 157 | } 158 | } 159 | } 160 | } 161 | 162 | return nil 163 | } 164 | 165 | scope = module.ModScope 166 | } 167 | 168 | if r := scope.Idents[name.Name]; r != nil { 169 | return r 170 | } else if r := scope.UsedModules[name.Name]; r != nil { 171 | return &Ident{IDENT_MODULE, r, true, v} 172 | } else if v.Outer != nil { 173 | return v.Outer.GetIdent(name) 174 | } 175 | 176 | return nil 177 | } 178 | 179 | func (v *Scope) Dump(depth int) { 180 | indent := strings.Repeat(" ", depth) 181 | 182 | if depth == 0 { 183 | log.Debug("resolve", indent) 184 | log.Debugln("resolve", "This scope:") 185 | } 186 | 187 | for name, ident := range v.Idents { 188 | log.Debug("resolve", indent) 189 | log.Debugln("resolve", " %s (%s)", name, ident.Type) 190 | } 191 | 192 | if v.Outer != nil { 193 | log.Debug("resolve", indent) 194 | log.Debugln("resolve", "Parent scope:") 195 | v.Outer.Dump(depth + 1) 196 | } 197 | 198 | } 199 | -------------------------------------------------------------------------------- /lib/std/strings/string.ark: -------------------------------------------------------------------------------- 1 | #use std::adt 2 | #use std::mem 3 | #use std::unicode::utf8 4 | 5 | /// A dynamic, unicode decoded string. 6 | pub type String struct { 7 | data: ^mut adt::List, 8 | }; 9 | 10 | /// Creates a heap-allocated, dynamic string 11 | /// from the given static string. 12 | /// 13 | /// ## Example 14 | /// ``` 15 | /// #use std::string 16 | /// 17 | /// myString := strings::String::from("Hello, World!"); 18 | /// defer myString.destroy(); 19 | /// ``` 20 | pub func (String) from(source: string) -> ^mut String { 21 | str := String::withCapacity(len(source)); 22 | mut idx := uint(0); 23 | for idx < len(source) { 24 | char := []u8{source[idx]}; 25 | decodedChar := []rune{0}; 26 | utf8::decode(decodedChar, char); 27 | str.append(decodedChar[0]); 28 | idx += 1; 29 | } 30 | return str; 31 | } 32 | 33 | /// Creates a zeroed out string with the given 34 | /// capacity. 35 | /// 36 | /// ## Examples 37 | /// ``` 38 | /// #use std::string 39 | /// 40 | /// pub func main() -> int { 41 | /// buffer := strings::String::withCapacity(32); 42 | /// defer buffer.destroy(); 43 | /// 44 | /// mut idx := 0; 45 | /// for idx < 10 { 46 | /// buffer.concat("great moves!"); 47 | /// idx += 1; 48 | /// } 49 | /// 50 | /// return 0; 51 | /// } 52 | /// ``` 53 | pub func (String) withCapacity(size: uint) -> ^mut String { 54 | str := mem::alloc(); 55 | @str = String { 56 | data: adt::List::newWithCapacity(size), 57 | }; 58 | 59 | mut idx := uint(0); 60 | for idx < size { 61 | str.append('\0'); 62 | idx += 1; 63 | } 64 | 65 | return str; 66 | } 67 | 68 | /// Creates a new, empty string 69 | /// 70 | /// ## Examples 71 | /// ``` 72 | /// #use std::string 73 | /// 74 | /// pub func main() -> int { 75 | /// buffer := strings::String::new(); 76 | /// defer buffer.destroy(); 77 | /// return 0; 78 | /// } 79 | /// ``` 80 | pub func (String) new() -> ^mut String { 81 | return String::withCapacity(0); 82 | } 83 | 84 | /// Clear the entire string returning it 85 | /// back to a length of zero 86 | /// 87 | /// ## Examples 88 | /// ``` 89 | /// #use std::string 90 | /// #use std::io 91 | /// 92 | /// pub func main() -> int { 93 | /// johnsLeftString := strings::String::from("Hello, World!"); 94 | /// defer johnsLeftString.destroy(); 95 | /// 96 | /// io::printDynamicString(johnsLeftString); 97 | /// johnsLeftString.clear(); 98 | /// io::printDynamicString(johnsLeftString); 99 | /// 100 | /// return 0; 101 | /// } 102 | /// ``` 103 | pub func (a: ^String) clear() { 104 | mut idx := uint(0); 105 | for idx < a.length() { 106 | a.pop(); 107 | idx += 1; 108 | } 109 | } 110 | 111 | pub func (a: ^String) toRawPointer() -> ^rune { 112 | return a.data.toRawPointer(); 113 | } 114 | 115 | /// Pops and returns the last rune 116 | /// in the string wrapped in an Option type. 117 | /// 118 | /// ## Examples 119 | /// 120 | /// ``` 121 | /// #use std::string 122 | /// #use std::io 123 | /// 124 | /// pub func main() -> int { 125 | /// sentence := strings::String::from("hey jude, how are ya?"); 126 | /// end := string.pop(); 127 | /// io::printRune(end.unwrap()); 128 | /// return 0; 129 | /// } 130 | /// ``` 131 | pub func (a: ^String) pop() -> Option { 132 | if a.length() == 0 { 133 | return Option::None; 134 | } 135 | return a.data.pop(); 136 | } 137 | 138 | /// Append a static, fixed length string 139 | /// to the end of a dynamic string 140 | /// 141 | /// ## Examples 142 | /// 143 | /// ``` 144 | /// #use std::string 145 | /// #use std::io 146 | /// 147 | /// pub func main() -> int { 148 | /// part := strings::String::from("Hello, "); 149 | /// part.concat("world!"); 150 | /// io::printDynamicString(part); 151 | /// return 0; 152 | /// } 153 | /// ``` 154 | pub func (a: ^String) concat(other: string) { 155 | mut idx := uint(0); 156 | for idx < len(other) { 157 | otherRunePointer := uintptr(other[idx]); 158 | a.append((rune)(otherRunePointer)); 159 | idx += 1; 160 | } 161 | } 162 | 163 | /// Appends a rune to the end of 164 | /// the given string 165 | /// 166 | /// ## Examples 167 | /// 168 | /// ``` 169 | /// #use std::string 170 | /// #use std::io 171 | /// 172 | /// pub func main() -> int { 173 | /// junk := []rune{'a', 'b', 'c'}; 174 | /// foo := strings::String::from("some letters: "); 175 | /// io::printDynamicString(foo); 176 | /// 177 | /// mut idx := uint(0); 178 | /// for idx < len(junk) { 179 | /// if idx != 0 { 180 | /// foo.append(','); 181 | /// } 182 | /// foo.append(junk[idx]); 183 | /// idx += 1; 184 | /// } 185 | /// 186 | /// io::printDynamicString(foo); 187 | /// return 0; 188 | /// } 189 | /// ``` 190 | pub func (a: ^String) append(char: rune) { 191 | a.data.append(char); 192 | } 193 | 194 | /// Returns the rune at the given index 195 | /// wrapped in an Option type. 196 | /// 197 | /// ## Examples 198 | /// 199 | /// ``` 200 | /// #use std::string 201 | /// #use std::io 202 | /// 203 | /// pub func main() -> int { 204 | /// foo := strings::String::from("apples, tomatos, oranges"); 205 | /// mut idx := uint(0); 206 | /// for idx < foo.length() { 207 | /// char := foo.at(idx); 208 | /// io::printRune(char.unwrap()); 209 | /// idx += 1; 210 | /// } 211 | /// return 0; 212 | /// } 213 | /// ``` 214 | pub func (a: ^String) get(index: uint) -> Option { 215 | if index < 0 || index > a.length() { 216 | return Option::None; 217 | } 218 | return a.data.get(index); 219 | } 220 | 221 | pub func (a: ^String) set(val: rune, where: uint) { 222 | a.data.set(val, where); 223 | } 224 | 225 | /// Returns the length of the string 226 | /// note that a string is dynamic, and this 227 | /// is not the amount of memory the string 228 | /// has reversed, just how much of that memory 229 | /// it is taking up. 230 | /// 231 | /// ## Examples 232 | /// 233 | /// ``` 234 | /// #use std::string 235 | /// 236 | /// pub func main() -> int { 237 | /// foo := strings::String::from("Ark Language!"); 238 | /// return foo.length(); 239 | /// } 240 | /// ``` 241 | pub func (a: ^String) length() -> uint { 242 | return a.data.getLength(); 243 | } 244 | 245 | /// Destroys the string 246 | /// 247 | /// ## Examples 248 | /// 249 | /// ``` 250 | /// #use std::string 251 | /// 252 | /// pub func main() -> int { 253 | /// name := strings::String::from("Felix"); 254 | /// name.destroy(); 255 | /// return 0; 256 | /// } 257 | /// ``` 258 | pub func (a: ^String) destroy() { 259 | a.data.destroy(); 260 | mem::free(a); 261 | } 262 | --------------------------------------------------------------------------------