diff --git a/README.md b/README.md index 194e57c..8758f41 100644 --- a/README.md +++ b/README.md @@ -2,6 +2,11 @@ A very cool language +## Huh? +* Indentation-based syntax +* Compiled to x86_64 Assembly +* Sometimes works + ## Syntax ```go func fib[n: I64] : I64 diff --git a/examples/euler1.zr b/examples/euler1.zr index 5e55a6d..a8a12a3 100644 --- a/examples/euler1.zr +++ b/examples/euler1.zr @@ -1,6 +1,3 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - func main[] : I64 let sum: I64 = 0 diff --git a/examples/euler10.zr b/examples/euler10.zr index db23dff..6ca0106 100644 --- a/examples/euler10.zr +++ b/examples/euler10.zr @@ -1,27 +1,9 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - -func is_prime[n: I64]: I64 - if n <= 1 - return false - if n == 2 || n == 3 - return true - if n % 2 == 0 || n % 3 == 0 - return false - - let i: I64 = 5 - while i * i <= n - if n % i == 0 || n % (i + 2) == 0 - return false - i = i + 6 - return true - func main[] : I64 let sum: I64 = 0 let i: I64 = 0 while i < 2000000 - if is_prime(i) + if Math.is_prime(i) sum = sum + i i = i + 1 print_i64(sum) \ No newline at end of file diff --git a/examples/euler12.zr b/examples/euler12.zr index c18b91e..b703de1 100644 --- a/examples/euler12.zr +++ b/examples/euler12.zr @@ -1,6 +1,3 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - func num_divisors[n: I64] : I64 let end: I64 = isqrt(n) diff --git a/examples/euler14.zr b/examples/euler14.zr index 10fba0e..fc53f97 100644 --- a/examples/euler14.zr +++ b/examples/euler14.zr @@ -1,6 +1,3 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - func collatz_num[n: I64] : I64 if n % 2 == 0 return n / 2 diff --git a/examples/euler2.zr b/examples/euler2.zr index 64c4117..6bb61c0 100644 --- a/examples/euler2.zr +++ b/examples/euler2.zr @@ -1,6 +1,3 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - func main[] : I64 let sum: I64 = 0 let a: I64 = 0 diff --git a/examples/euler3.zr b/examples/euler3.zr index 9041a03..3dc3736 100644 --- a/examples/euler3.zr +++ b/examples/euler3.zr @@ -1,6 +1,3 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - func main[] : I64 let n: I64 = 600851475143 let f: I64 = 2 diff --git a/examples/euler4.zr b/examples/euler4.zr index 66b9c2a..2d187b5 100644 --- a/examples/euler4.zr +++ b/examples/euler4.zr @@ -1,11 +1,3 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - -func I64.to_string[n: I64] : String - let x: I64 = malloc(21) - sprintf(x, "%ld", n) - return x - func main[] : I64 let out: I64 = 0 diff --git a/examples/euler5.zr b/examples/euler5.zr index d259053..c7d7e8e 100644 --- a/examples/euler5.zr +++ b/examples/euler5.zr @@ -1,21 +1,8 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - -func gcd[a: I64, b: I64] : I64 - while b != 0 - let tmp: I64 = b - b = a % b - a = tmp - return a - -func lcm[a: I64, b: I64] : I64 - return (a * b) / gcd(a, b) - func main[] : I64 let out: I64 = 1 let i: I64 = 1 while i < 21 - out = lcm(out, i) + out = Math.lcm(out, i) i = i + 1 print_i64(out) \ No newline at end of file diff --git a/examples/euler6.zr b/examples/euler6.zr index ace9130..eded52f 100644 --- a/examples/euler6.zr +++ b/examples/euler6.zr @@ -1,6 +1,3 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - func main[] : I64 let sum_of_squares: I64 = 0 let i: I64 = 1 diff --git a/examples/euler7.zr b/examples/euler7.zr index b4b08a0..821b0fd 100644 --- a/examples/euler7.zr +++ b/examples/euler7.zr @@ -1,27 +1,9 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - -func is_prime[n: I64]: I64 - if n <= 1 - return false - if n == 2 || n == 3 - return true - if n % 2 == 0 || n % 3 == 0 - return false - - let i: I64 = 5 - while i * i <= n - if n % i == 0 || n % (i + 2) == 0 - return false - i = i + 6 - return true - func main[] : I64 let found: I64 = 0 let i: I64 = 1 while 1 - if is_prime(i) + if Math.is_prime(i) found = found + 1 if found == 10001 print_i64(i) diff --git a/examples/euler8.zr b/examples/euler8.zr index df3a131..9f5871e 100644 --- a/examples/euler8.zr +++ b/examples/euler8.zr @@ -1,9 +1,3 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - -func char_to_i64[c: I64]: I64 - return c - 48 - func main[] : I64 let n: String = "7316717653133062491922511967442657474235534919493496983520312774506326239578318016984801869478851843858615607891129494954595017379583319528532088055111254069874715852386305071569329096329522744304355766896648950445244523161731856403098711121722383113622298934233803081353362766142828064444866452387493035890729629049156044077239071381051585930796086670172427121883998797908792274921901699720888093776657273330010533678812202354218097512545405947522435258490771167055601360483958644670632441572215539753697817977846174064955149290862569321978468622482839722413756570560574902614079729686524145351004748216637048440319989000889524345065854122758866688116427171479924442928230863465674813919123162824586178664583591245665294765456828489128831426076900422421902267105562632111110937054421750694165896040807198403850962455444362981230987879927244284909188845801561660979191338754992005240636899125607176060588611646710940507754100225698315520005593572972571636269561882670428252483600823257530420752963450" @@ -14,7 +8,7 @@ func main[] : I64 let s: I64 = 1 let j: I64 = 0 while j < 13 - s = s * char_to_i64(nth(n, i + j)) + s = s * Char.to_i64(nth(n, i + j)) j = j + 1 if s > out out = s diff --git a/examples/euler9.zr b/examples/euler9.zr index 02a460b..b37c2a7 100644 --- a/examples/euler9.zr +++ b/examples/euler9.zr @@ -1,6 +1,3 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - func main[] : I64 let a: I64 = 1 while a < 1000 diff --git a/examples/fib.zr b/examples/fib.zr index 9d25f36..afe69ec 100644 --- a/examples/fib.zr +++ b/examples/fib.zr @@ -1,6 +1,3 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - func main[] : I64 let a: I64 = 0 let b: I64 = 1 diff --git a/examples/strings.zr b/examples/strings.zr index f6ba059..ad28f4f 100644 --- a/examples/strings.zr +++ b/examples/strings.zr @@ -1,12 +1,7 @@ -func print_i64[x: I64] : I64 - printf("%ld\n", x) - -func I64.to_string[n: I64] : String - let x: String = malloc(21) - sprintf(x, "%ld", n) - return x - func main[] : I64 - let s: String = I64.to_string(58394) - print_i64(strlen(s)) - free(s) \ No newline at end of file + let a: String = I64.to_string(58394) + print_i64(strlen(a)) + let b: String = concat(a, "test") + print_i64(strlen(b)) + free(a) + free(b) \ No newline at end of file diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index e5cf401..599f066 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -101,11 +101,14 @@ extern printf extern sprintf extern strlen extern strcmp +extern strcat +extern strcpy +extern strdup extern puts extern system -print equ puts +extern exit +copystr equ strdup -; generated with clang strrev: push r14 push rbx @@ -118,15 +121,15 @@ strrev: mov rcx, rax mov rsi, r14 mov rdx, r14 -.LBB0_1: +.strrev.1: sub rdx, 1 - jb .LBB0_2 + jb .strrev.2 mov sil, byte [rbx + rsi - 1] mov byte [rcx], sil inc rcx mov rsi, rdx - jmp .LBB0_1 -.LBB0_2: + jmp .strrev.1 +.strrev.2: mov byte [rax + r14], 0 add rsp, 8 pop rbx @@ -138,30 +141,30 @@ isqrt: mov rcx, 1 mov rbx, rdi shl rcx, 62 -.LBB0_3: +.isqrt.1: cmp rcx, 0 - je .LBB0_5 + je .isqrt.5 cmp rcx, rbx - jbe .LBB0_4 + jbe .isqrt.2 shr rcx, 2 - jmp .LBB0_3 -.LBB0_4: + jmp .isqrt.1 +.isqrt.2: cmp rcx, 0 - je .LBB0_5 + je .isqrt.5 mov rdx, rax add rdx, rcx cmp rbx, rdx - jb .LBB0_7 + jb .isqrt.3 sub rbx, rdx shr rax, 1 add rax, rcx - jmp .LBB0_6 -.LBB0_7: + jmp .isqrt.4 +.isqrt.3: shr rax, 1 -.LBB0_6: +.isqrt.4: shr rcx, 2 - jmp .LBB0_4 -.LBB0_5: + jmp .isqrt.2 +.isqrt.5: ret nth: @@ -259,10 +262,8 @@ nth: self.compile_stmt(env, *body)?; - if name.lexeme == "main" { - // default exit code - emit!(&mut self.output, " mov rax, 0"); - } + // TODO: default exit code only for main + emit!(&mut self.output, " mov rax, 0"); emit!(&mut self.output, " mov rsp, rbp"); emit!(&mut self.output, " pop rbp"); @@ -356,17 +357,22 @@ nth: TokenType::String => { // TODO: actual string parsing in the tokenizer let value = &token.lexeme[1..token.lexeme.len() - 1].replace("\\n", "\n"); - let charcodes = value - .chars() - .map(|x| (x as u8).to_string()) - .collect::>() - .join(","); - emit!( - &mut self.data_section, - " S{} db {},0", - self.data_counter, - charcodes, - ); + + if value.is_empty() { + emit!(&mut self.data_section, " S{} db 0", self.data_counter); + } else { + let charcodes = value + .chars() + .map(|x| (x as u8).to_string()) + .collect::>() + .join(","); + emit!( + &mut self.data_section, + " S{} db {},0", + self.data_counter, + charcodes, + ); + } emit!(&mut self.output, " mov rax, S{}", self.data_counter); self.data_counter += 1; } diff --git a/src/main.rs b/src/main.rs index e6f2d18..fb9e012 100644 --- a/src/main.rs +++ b/src/main.rs @@ -10,6 +10,23 @@ use std::{ use tokenizer::ZernError; +fn compile_file_to( + codegen: &mut codegen_x86_64::CodegenX86_64, + filename: &str, + source: String, +) -> Result<(), ZernError> { + let tokenizer = tokenizer::Tokenizer::new(filename.to_owned(), source); + let tokens = tokenizer.tokenize()?; + + let parser = parser::Parser::new(tokens); + let statements = parser.parse()?; + + for stmt in statements { + codegen.compile_stmt(&mut codegen_x86_64::Env::new(), stmt)?; + } + Ok(()) +} + fn compile_file(path: String) -> Result<(), ZernError> { let source = match fs::read_to_string(path.clone()) { Ok(x) => x, @@ -21,18 +38,10 @@ fn compile_file(path: String) -> Result<(), ZernError> { let filename = Path::new(&path).file_name().unwrap().to_str().unwrap(); - let tokenizer = tokenizer::Tokenizer::new(filename.to_owned(), source); - let tokens = tokenizer.tokenize()?; - - let parser = parser::Parser::new(tokens); - let statements = parser.parse()?; - let mut codegen = codegen_x86_64::CodegenX86_64::new(); - codegen.emit_prologue()?; - for stmt in statements { - codegen.compile_stmt(&mut codegen_x86_64::Env::new(), stmt)?; - } + compile_file_to(&mut codegen, "std.zr", include_str!("std.zr").into())?; + compile_file_to(&mut codegen, filename, source)?; codegen.emit_epilogue()?; if fs::write("out.s", codegen.get_output()).is_err() { diff --git a/src/std.zr b/src/std.zr new file mode 100644 index 0000000..6175172 --- /dev/null +++ b/src/std.zr @@ -0,0 +1,67 @@ +func print[x: String] : I64 + printf("%s\n", x) + +func print_i64[x: I64] : I64 + printf("%ld\n", x) + +func concat[a: String, b: String] : String + let c: String = malloc(strlen(a) + strlen(b) + 1) + strcpy(c, a) + strcat(c, b) + return c + +func Char.to_i64[c: I64]: I64 + return c - 48 + +func I64.to_string[n: I64] : String + let x: I64 = malloc(21) + sprintf(x, "%ld", n) + return x + +func Math.gcd[a: I64, b: I64] : I64 + while b != 0 + let tmp: I64 = b + b = a % b + a = tmp + return a + +func Math.min[a: I64, b: I64] : I64 + if a < b + return a + return b + +func Math.max[a: I64, b: I64] : I64 + if a > b + return a + return b + +func Math.abs[n: I64] : I64 + if n < 0 + return -n + return n + +func Math.pow[b: I64, e: I64] : I64 + let out: I64 = 1 + let i: I64 = 0 + while i < e + out = out * b + i = i + 1 + return out + +func Math.lcm[a: I64, b: I64] : I64 + return (a * b) / Math.gcd(a, b) + +func Math.is_prime[n: I64]: I64 + if n <= 1 + return false + if n == 2 || n == 3 + return true + if n % 2 == 0 || n % 3 == 0 + return false + + let i: I64 = 5 + while i * i <= n + if n % i == 0 || n % (i + 2) == 0 + return false + i = i + 6 + return true \ No newline at end of file diff --git a/src/tokenizer.rs b/src/tokenizer.rs index e911fca..c638a2d 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -220,7 +220,7 @@ impl Tokenizer { self.handle_indentation()?; } '0'..='9' => self.scan_number(), - 'A'..='Z' | 'a'..='z' | '_' => self.scan_identifier(), + 'A'..='Z' | 'a'..='z' | '_' | '.' => self.scan_identifier(), _ => return error!(self.loc, "unexpected character"), } Ok(()) @@ -295,7 +295,11 @@ impl Tokenizer { } fn scan_identifier(&mut self) { - while self.peek().is_alphanumeric() || self.peek() == '_' || self.peek() == '.' { + while self.peek().is_alphanumeric() + || self.peek() == '_' + || self.peek() == '.' + || self.peek() == '!' + { self.advance(); } diff --git a/test.py b/test.py new file mode 100644 index 0000000..193900d --- /dev/null +++ b/test.py @@ -0,0 +1,21 @@ +import os +import sys +import time + +os.system("cargo build --release") + +for x in os.listdir("examples/"): + print( + "\033[93mBuilding %s...\033[0m" % x, + end="", + flush=True, + ) + t = time.time() + if os.system("./target/release/zern examples/" + x) != 0: + sys.exit(1) + print(" %sms" % round((time.time() - t) * 1000, 2)) + + t = time.time() + if os.system("./out") != 0: + sys.exit(1) + print("\033[93mRunning %s...\033[0m %sms" % (x, round((time.time() - t) * 1000, 2)))