diff --git a/examples/brainfuck.zr b/examples/brainfuck.zr index 1f203ec..49a366a 100644 --- a/examples/brainfuck.zr +++ b/examples/brainfuck.zr @@ -27,7 +27,7 @@ func main[] : i64 if !memory[p] i = i + 1 let opened = 0 - while i < src_len & !(src[i] == ']' & !opened) + while i < src_len && !(src[i] == ']' && !opened) if src[i] == '[' opened = opened + 1 else if src[i] == ']' @@ -37,7 +37,7 @@ func main[] : i64 if memory[p] i = i - 1 let closed = 0 - while i >= 0 & !(src[i] == '[' & !closed) + while i >= 0 && !(src[i] == '[' && !closed) if src[i] == ']' closed = closed + 1 else if src[i] == '[' diff --git a/examples/curl.zr b/examples/curl.zr index ea79b3d..5f970b1 100644 --- a/examples/curl.zr +++ b/examples/curl.zr @@ -41,14 +41,14 @@ func main[argc: i64, argv: ptr] : i64 let found: bool = false let end_index: i64 = -1 - while !found & header_size < 8192 + while !found && header_size < 8192 let n: i64 = net.read(s, header_buf + header_size, 8192 - header_size) if n <= 0 break let current_size: i64 = header_size + n i = 0 while i <= current_size - 4 - if header_buf[i] == 13 & header_buf[i + 1] == 10 & header_buf[i + 2] == 13 & header_buf[i + 3] == 10 + if header_buf[i] == 13 && header_buf[i + 1] == 10 && header_buf[i + 2] == 13 && header_buf[i + 3] == 10 found = true end_index = i + 4 break diff --git a/examples/puzzles/aoc2024_02.zr b/examples/puzzles/aoc2024_02.zr index 53a6238..07d3002 100644 --- a/examples/puzzles/aoc2024_02.zr +++ b/examples/puzzles/aoc2024_02.zr @@ -2,13 +2,13 @@ func check[report: array] : bool let increasing: bool = array.nth(report, 0) < array.nth(report, 1) for i in 0..array.size(report)-1 - if array.nth(report, i) > array.nth(report, i + 1) & increasing + if array.nth(report, i) > array.nth(report, i + 1) && increasing return false - if array.nth(report, i) < array.nth(report, i + 1) & !increasing + if array.nth(report, i) < array.nth(report, i + 1) && !increasing return false let diff: i64 = math.abs(array.nth(report, i) - array.nth(report, i + 1)) - if diff < 1 | diff > 3 + if diff < 1 || diff > 3 return false return true diff --git a/examples/puzzles/aoc2024_04.zr b/examples/puzzles/aoc2024_04.zr index 70a97c1..c54a9fc 100644 --- a/examples/puzzles/aoc2024_04.zr +++ b/examples/puzzles/aoc2024_04.zr @@ -1,5 +1,5 @@ func check[lines: array, x: i64, y: i64, dx: i64, dy: i64] : bool - if x + dx * 3 < 0 | x + dx * 3 >= array.size(lines) | y + dy * 3 < 0 | y + dy * 3 >= str.len(array.nth(lines, 0)) + if x + dx * 3 < 0 || x + dx * 3 >= array.size(lines) || y + dy * 3 < 0 || y + dy * 3 >= str.len(array.nth(lines, 0)) return false if array.nth(lines, x)[y] != 'X' @@ -50,7 +50,7 @@ func part2[lines: array] : void str.set(s, 3, array.nth(lines, x - 1)[y + 1]) str.set(s, 4, 0) - if str.equal(s, "MSSM") | str.equal(s, "SMMS") | str.equal(s, "MMSS") | str.equal(s, "SSMM") + if str.equal(s, "MSSM") || str.equal(s, "SMMS") || str.equal(s, "MMSS") || str.equal(s, "SSMM") out = out + 1 io.println_i64(out) diff --git a/examples/puzzles/aoc2024_05.zr b/examples/puzzles/aoc2024_05.zr index 683f0b7..de9ab56 100644 --- a/examples/puzzles/aoc2024_05.zr +++ b/examples/puzzles/aoc2024_05.zr @@ -1,6 +1,6 @@ func rule_exists[rules_left: array, rules_right: array, a: i64, b: i64] : bool for k in 0..array.size(rules_left) - if array.nth(rules_left, k) == a & array.nth(rules_right, k) == b + if array.nth(rules_left, k) == a && array.nth(rules_right, k) == b return true return false diff --git a/examples/puzzles/euler_01.zr b/examples/puzzles/euler_01.zr index 2f3eff5..c279f38 100644 --- a/examples/puzzles/euler_01.zr +++ b/examples/puzzles/euler_01.zr @@ -2,6 +2,6 @@ func main[] : i64 let sum = 0 for i in 0..1000 - if i % 5 == 0 | i % 3 == 0 + if i % 5 == 0 || i % 3 == 0 sum = sum + i io.println_i64(sum) \ No newline at end of file diff --git a/examples/puzzles/euler_19.zr b/examples/puzzles/euler_19.zr index d679ba7..9def4b9 100644 --- a/examples/puzzles/euler_19.zr +++ b/examples/puzzles/euler_19.zr @@ -1,10 +1,10 @@ func days[y: i64, m: i64] : i64 if m == 2 - if (((y % 4 == 0) & (y % 100 != 0)) | (y % 400 == 0)) + if (((y % 4 == 0) && (y % 100 != 0)) || (y % 400 == 0)) return 29 else return 28 - else if (m == 4) | (m == 6) | (m == 9) | (m == 11) + else if (m == 4) || (m == 6) || (m == 9) || (m == 11) return 30 else return 31 diff --git a/examples/puzzles/euler_21.zr b/examples/puzzles/euler_21.zr index eb6573b..c8be475 100644 --- a/examples/puzzles/euler_21.zr +++ b/examples/puzzles/euler_21.zr @@ -15,7 +15,7 @@ func main[] : i64 for i in 2..10000 let d: i64 = divisors_sum(i) - if i < d & i == divisors_sum(d) + if i < d && i == divisors_sum(d) sum = sum + i + d io.println_i64(sum) \ No newline at end of file diff --git a/examples/rule110.zr b/examples/rule110.zr index 9c6f84c..c66db77 100644 --- a/examples/rule110.zr +++ b/examples/rule110.zr @@ -11,7 +11,7 @@ func rule110_step[state: array] : array if i + 1 < state_len right = array.nth(state, i + 1) - array.push(new_state, !((!left & !center & !right) | (left & !center & !right) | (left & center & right))) + array.push(new_state, !((!left && !center && !right) || (left && !center && !right) || (left && center && right))) return new_state diff --git a/examples/tokenizer.zr b/examples/tokenizer.zr index ded1bac..286eb37 100644 --- a/examples/tokenizer.zr +++ b/examples/tokenizer.zr @@ -50,11 +50,11 @@ func handle_indentation[tokens: array, current: ptr, column: ptr, line: i64, sou array.push(indent_stack, new_indent) add_token_with_lexeme("Indent", tokens, "", line, mem.read64(column)) else if new_indent < mem.read64(current_indent) - while array.size(indent_stack) > 1 & array.nth(indent_stack, array.size(indent_stack) - 1) > new_indent + while array.size(indent_stack) > 1 && array.nth(indent_stack, array.size(indent_stack) - 1) > new_indent array.pop(indent_stack) add_token_with_lexeme("Dedent", tokens, "", line, mem.read64(column)) - if array.size(indent_stack) == 0 | array.nth(indent_stack, array.size(indent_stack) - 1) != new_indent + if array.size(indent_stack) == 0 || array.nth(indent_stack, array.size(indent_stack) - 1) != new_indent zern_error(filename, line, mem.read64(column), "invalid indentation") mem.write64(current_indent, new_indent) @@ -75,14 +75,14 @@ func scan_number[current: ptr, column: ptr, source: str, source_len: i64] : void while str.is_hex_digit(peek(mem.read64(current), source, source_len)) advance(current, column, source, source_len) else if match_char('o', current, column, source, source_len) - while peek(mem.read64(current), source, source_len) >= '0' & peek(mem.read64(current), source, source_len) <= '7' + while peek(mem.read64(current), source, source_len) >= '0' && peek(mem.read64(current), source, source_len) <= '7' advance(current, column, source, source_len) else while str.is_digit(peek(mem.read64(current), source, source_len)) advance(current, column, source, source_len) func scan_identifier[tokens: array, current: ptr, column: ptr, start: i64, line: i64, source: str, source_len: i64] : void - while str.is_alphanumeric(peek(mem.read64(current), source, source_len)) | peek(mem.read64(current), source, source_len) == '_' | peek(mem.read64(current), source, source_len) == '.' + while str.is_alphanumeric(peek(mem.read64(current), source, source_len)) || peek(mem.read64(current), source, source_len) == '_' || peek(mem.read64(current), source, source_len) == '.' advance(current, column, source, source_len) let len: i64 = mem.read64(current) - start @@ -216,7 +216,7 @@ func scan_token[tokens: array, current: ptr, line: ptr, column: ptr, source: str zern_error(filename, mem.read64(line), mem.read64(column), "unterminated string") advance(current, column, source, source_len) add_token("String", tokens, source, start, mem.read64(current), mem.read64(line), mem.read64(column)) - else if c == ' ' | c == 13 // \r + else if c == ' ' || c == 13 // \r return 0 else if c == 10 // \n mem.write64(line, mem.read64(line) + 1) @@ -225,7 +225,7 @@ func scan_token[tokens: array, current: ptr, line: ptr, column: ptr, source: str else if str.is_digit(c) scan_number(current, column, source, source_len) add_token("Number", tokens, source, start, mem.read64(current), mem.read64(line), mem.read64(column)) - else if str.is_letter(c) | c == '_' + else if str.is_letter(c) || c == '_' scan_identifier(tokens, current, column, start, mem.read64(line), source, source_len) else zern_error(filename, mem.read64(line), mem.read64(column), "unexpected character") diff --git a/src/analyzer.rs b/src/analyzer.rs index ad2dfb1..91f375f 100644 --- a/src/analyzer.rs +++ b/src/analyzer.rs @@ -120,6 +120,10 @@ impl Analyzer { self.analyze_expr(left)?; self.analyze_expr(right)?; } + Expr::Logical { left, op: _, right } => { + self.analyze_expr(left)?; + self.analyze_expr(right)?; + } Expr::Grouping(expr) => self.analyze_expr(expr)?, Expr::Literal(_) => {} Expr::Unary { op: _, right } => { diff --git a/src/codegen_x86_64.rs b/src/codegen_x86_64.rs index 73bf600..70e35b6 100644 --- a/src/codegen_x86_64.rs +++ b/src/codegen_x86_64.rs @@ -395,6 +395,25 @@ _builtin_environ: _ => unreachable!(), } } + Expr::Logical { left, op, right } => { + let end_label = self.label(); + match op.token_type { + TokenType::LogicalAnd => { + self.compile_expr(env, *left)?; + emit!(&mut self.output, " test rax, rax"); + emit!(&mut self.output, " je {}", end_label); + self.compile_expr(env, *right)?; + } + TokenType::LogicalOr => { + self.compile_expr(env, *left)?; + emit!(&mut self.output, " test rax, rax"); + emit!(&mut self.output, " jne {}", end_label); + self.compile_expr(env, *right)?; + } + _ => unreachable!(), + } + emit!(&mut self.output, "{}:", end_label); + } Expr::Grouping(expr) => self.compile_expr(env, *expr)?, Expr::Literal(token) => match token.token_type { TokenType::Number => { diff --git a/src/parser.rs b/src/parser.rs index 1569941..9c6e8f7 100644 --- a/src/parser.rs +++ b/src/parser.rs @@ -54,6 +54,11 @@ pub enum Expr { op: Token, right: Box, }, + Logical { + left: Box, + op: Token, + right: Box, + }, Grouping(Box), Literal(Token), Unary { @@ -341,10 +346,10 @@ impl Parser { fn or_and(&mut self) -> Result { let mut expr = self.equality()?; - while self.match_token(&[TokenType::BitOr, TokenType::BitAnd]) { + while self.match_token(&[TokenType::LogicalOr, TokenType::LogicalAnd]) { let op = self.previous().clone(); let right = self.equality()?; - expr = Expr::Binary { + expr = Expr::Logical { left: Box::new(expr), op, right: Box::new(right), @@ -394,7 +399,13 @@ impl Parser { fn term(&mut self) -> Result { let mut expr = self.factor()?; - while self.match_token(&[TokenType::Plus, TokenType::Minus, TokenType::Xor]) { + while self.match_token(&[ + TokenType::Plus, + TokenType::Minus, + TokenType::Xor, + TokenType::BitAnd, + TokenType::BitOr, + ]) { let op = self.previous().clone(); let right = self.factor()?; expr = Expr::Binary { diff --git a/src/std.zr b/src/std.zr index 47851ef..2ab705b 100644 --- a/src/std.zr +++ b/src/std.zr @@ -113,32 +113,32 @@ func str.set[s: str, n: i64, c: u8] : void func str.equal[a: str, b: str] : bool let i = 0 - while a[i] != 0 & b[i] != 0 + while a[i] != 0 && b[i] != 0 if a[i] != b[i] return false i = i + 1 return a[i] == b[i] func str.is_whitespace[x: u8] : bool - return x == ' ' | x == 10 | x == 13 | x == 9 + return x == ' ' || x == 10 || x == 13 || x == 9 func str.is_digit[x: u8] : bool - return x >= '0' & x <= '9' + return x >= '0' && x <= '9' func str.is_hex_digit[x: u8] : bool - return (x >= '0' & x <= '9') | (x >= 'a' & x <= 'f') | (x >= 'A' & x <= 'F') + return (x >= '0' && x <= '9') || (x >= 'a' && x <= 'f') || (x >= 'A' && x <= 'F') func str.is_lowercase[x: u8] : bool - return x >= 'a' & x <= 'z' + return x >= 'a' && x <= 'z' func str.is_uppercase[x: u8] : bool - return x >= 'A' & x <= 'Z' + return x >= 'A' && x <= 'Z' func str.is_letter[x: u8] : bool - return str.is_uppercase(x) | str.is_lowercase(x) + return str.is_uppercase(x) || str.is_lowercase(x) func str.is_alphanumeric[x: u8] : bool - return str.is_letter(x) | str.is_digit(x) + return str.is_letter(x) || str.is_digit(x) func str.concat[a: str, b: str] : str let a_len: i64 = str.len(a) @@ -169,7 +169,7 @@ func str.find[haystack: str, needle: str] : i64 return -1 func str.substr[s: str, start: i64, length: i64] : str - if start < 0 | length < 0 | start + length > str.len(s) + if start < 0 || length < 0 || start + length > str.len(s) dbg.panic("str.substr out of bounds") let out: str = mem.alloc(length + 1) @@ -186,10 +186,10 @@ func str.trim[s: str] : str let start = 0 let end: i64 = len - 1 - while start <= end & str.is_whitespace(s[start]) + while start <= end && str.is_whitespace(s[start]) start = start + 1 - while end >= start & str.is_whitespace(s[end]) + while end >= start && str.is_whitespace(s[end]) end = end - 1 return str.substr(s, start, end - start + 1) @@ -269,14 +269,14 @@ func str.parse_i64[s: str] : i64 let i = 0 let sign = 1 - if i < len & s[i] == '-' + if i < len && s[i] == '-' sign = -1 i = i + 1 let num = 0 while i < len let d: u8 = s[i] - if d < '0' | d > '9' + if d < '0' || d > '9' break num = num * 10 + (d - '0') i = i + 1 @@ -299,9 +299,9 @@ func str.hex_encode[s: str] : str return out func str._hex_digit_to_int[d: u8] : i64 - if d >= 'a' & d <= 'f' + if d >= 'a' && d <= 'f' return d - 'a' + 10 - if d >= 'A' & d <= 'F' + if d >= 'A' && d <= 'F' return d - 'A' + 10 return d - '0' @@ -353,7 +353,7 @@ func math.lcm[a: i64, b: i64] : i64 func math.isqrt[n: i64] : i64 if n < 0 dbg.panic("negative number passed to math.isqrt") - if n == 0 | n == 1 + if n == 0 || n == 1 return n let guess: i64 = n @@ -368,14 +368,14 @@ func math.isqrt[n: i64] : i64 func math.is_prime[n: i64]: bool if n <= 1 return false - if n == 2 | n == 3 + if n == 2 || n == 3 return true - if n % 2 == 0 | n % 3 == 0 + if n % 2 == 0 || n % 3 == 0 return false let i = 5 while i * i <= n - if n % i == 0 | n % (i + 2) == 0 + if n % i == 0 || n % (i + 2) == 0 return false i = i + 6 return true @@ -387,13 +387,13 @@ func array.new[] : array return arr func array.nth[xs: array, n: i64] : i64 - if n < 0 | n >= array.size(xs) + if n < 0 || n >= array.size(xs) dbg.panic("array.nth out of bounds") let data: ptr = mem.read64(xs) return mem.read64(data + n * 8) func array.set[xs: array, n: i64, x: i64] : void - if n < 0 | n >= array.size(xs) + if n < 0 || n >= array.size(xs) dbg.panic("array.set out of bounds") let data: ptr = mem.read64(xs) mem.write64(data + n * 8, x) @@ -433,7 +433,7 @@ func array.pop[xs: array] : i64 return x func array.slice[xs: array, start: i64, length: i64] : array - if start < 0 | length < 0 | start + length > array.size(xs) + if start < 0 || length < 0 || start + length > array.size(xs) dbg.panic("array.slice out of bounds") let new_array: array = [] diff --git a/src/tokenizer.rs b/src/tokenizer.rs index 6e9091c..3dd93e8 100644 --- a/src/tokenizer.rs +++ b/src/tokenizer.rs @@ -17,6 +17,8 @@ pub enum TokenType { Colon, BitAnd, BitOr, + LogicalAnd, + LogicalOr, Pipe, At, DoubleDot, @@ -173,10 +175,18 @@ impl Tokenizer { self.add_token(TokenType::Slash) } } - '&' => self.add_token(TokenType::BitAnd), + '&' => { + if self.match_char('&') { + self.add_token(TokenType::LogicalAnd) + } else { + self.add_token(TokenType::BitAnd) + } + } '|' => { if self.match_char('>') { self.add_token(TokenType::Pipe); + } else if self.match_char('|') { + self.add_token(TokenType::LogicalOr); } else { self.add_token(TokenType::BitOr); } diff --git a/test.zr b/test.zr index 68341f0..2e2f30e 100644 --- a/test.zr +++ b/test.zr @@ -1,5 +1,5 @@ func run_test[x: str] : void - if str.equal(x, "puzzles") | str.equal(x, "raylib.zr") | str.equal(x, "x11.zr") | str.equal(x, "sqlite_todo.zr") + if str.equal(x, "puzzles") || str.equal(x, "raylib.zr") || str.equal(x, "x11.zr") || str.equal(x, "sqlite_todo.zr") io.print("\033[93mSkipping ") io.print(x) io.println("...\033[0m") @@ -19,7 +19,7 @@ func run_test[x: str] : void io.print_i64(build_end_time - build_start_time) io.println("ms") - if str.find(x, "/aoc") != -1 | str.equal(x, "guess_number.zr") | str.equal(x, "tcp_server.zr") + if str.find(x, "/aoc") != -1 || str.equal(x, "guess_number.zr") || str.equal(x, "tcp_server.zr") io.print("\033[93mSkipping ") io.print(x) io.println("...\033[0m") @@ -33,6 +33,9 @@ func run_test[x: str] : void if str.equal(x, "curl.zr") if os.shell("./out http://example.com") != 0 os.exit(1) + else if str.equal(x, "tokenizer.zr") + if os.shell("./out examples/tokenizer.zr") != 0 + os.exit(1) else if os.shell("./out") != 0 os.exit(1)