Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions assets/highlighting-tests/markdown.md
Original file line number Diff line number Diff line change
Expand Up @@ -84,3 +84,9 @@ export function greet(name) {
def greet(name: str) -> str:
return f"hello {name}"
```

```odin
greet :: proc(name: string) -> string {
return fmt.tprintf("hello %s", name)
}
```
259 changes: 259 additions & 0 deletions assets/highlighting-tests/odin.odin
Original file line number Diff line number Diff line change
@@ -0,0 +1,259 @@
// Single-line comment

/*
* Multi-line
* comment
*/

#+feature dynamic-literals
package main

import "core:fmt"

// Package-level constants
MY_CONST :: 42
PI :: 3.14159
NAME :: "Odin"

// Struct type
Vector3 :: struct {
x, y, z: f32,
}

// Union type
Value :: union {
int,
f64,
string,
}

// Enum type
Direction :: enum {
North,
South,
East,
West,
}

// Bit set
Flags :: bit_set[Direction]

// Distinct type
Meters :: distinct f32

// Procedures
add :: proc(a, b: int) -> int {
return a + b
}

variadic :: proc(nums: ..int) -> int {
result := 0
for n in nums {
result += n
}
return result
}

get_value :: proc() -> (int, bool) {
return 42, true
}

@(deprecated = "use new_func instead")
old_func :: proc() {
fmt.println("old")
}

demo_numbers :: proc() {
// Integer and float literals
_ := 42
_ := 3.14
_ := 1_000_000
_ := 0xFF
_ := 0b1010
_ := 0o77
_ := 1.5e-3
_ := 2i // imaginary

// Language constants
_ := true
_ := false
_ = nil
}

demo_strings :: proc() {
// Double-quoted strings with escape sequences
_ := "hello, world"
_ := "escape: \" \n \t \\"

// Raw strings (backtick — no escape processing)
_ := `C:\Windows\notepad.exe`
_ := `no \n escapes here`

// Rune / character literals
_ := 'A'
_ := '\n'
_ := '世'
}

demo_control_flow :: proc() {
// If / else if / else
if true {
fmt.println("yes")
} else if false {
fmt.println("no")
} else {
fmt.println("maybe")
}

// C-style for loop
for i := 0; i < 10; i += 1 {
if i == 5 { continue }
if i == 8 { break }
}

// Range-based for loop
for i in 0..<10 {
fmt.println(i)
}

// Switch statement
x := 42
switch x {
case 1:
fmt.println("one")
case 2, 3:
fmt.println("two or three")
case:
fmt.println("other")
}

// Fallthrough
switch 0 {
case 0:
fallthrough
case 1:
fmt.println("zero or one")
}

// When (compile-time conditional)
when ODIN_OS == .Windows {
fmt.println("Windows")
} else {
fmt.println("other OS")
}

// Defer
defer fmt.println("deferred")

// or_else / or_break
val := get_value() or_else 0
_ = val

for {
get_value() or_break
}
}

demo_types :: proc() {
// Struct literal
v := Vector3{x = 1, y = 4, z = 9}
fmt.println(v)

// Union
val: Value = 137
if i, ok := val.(int); ok {
fmt.println(i)
}

// Enum and switch
d := Direction.North
switch d {
case .North:
fmt.println("north")
case .South, .East, .West:
fmt.println("other direction")
}

// Bit set
flags := Flags{.North, .East}
fmt.println(.North in flags)

// Distinct type
dist := Meters(100.0)
fmt.println(dist)

// Cast / transmute / auto_cast
x: int = cast(int)3.14
y := transmute([4]u8)u32(0xDEAD_BEEF)
z := auto_cast x
_, _ = y, z
}

demo_collections :: proc() {
// Dynamic array
arr := make([dynamic]int)
defer delete(arr)
append(&arr, 1, 2, 3)
fmt.println(arr)

// Map
m := make(map[string]int)
defer delete(m)
m["key"] = 99
fmt.println(m["key"])

// Pointer
val := 123
ptr := &val
ptr^ = 456
fmt.println(val)
}

demo_builtins :: proc() {
// typeid / type_of
_ = typeid_of(int)
val := 0
_ = type_of(val)

// size_of / align_of / offset_of
_ = size_of(Vector3)
_ = align_of(f64)
_ = offset_of(Vector3, y)

// context
context.user_index = 1
}

demo_directives :: proc() {
// Compile-time assert
#assert(size_of(u8) == 1)

// SOA layout
v: #soa[4]Vector3
_ = v

// Partial switch
#partial switch d := Direction.North; d {
case .North:
fmt.println("north")
}

// Unroll for
#unroll for elem, idx in [3]int{1, 4, 9} {
fmt.println(elem, idx)
}
}

main :: proc() {
demo_numbers()
demo_strings()
demo_control_flow()
demo_types()
demo_collections()
demo_builtins()
demo_directives()

fmt.println(add(1, 2))
fmt.println(variadic(1, 2, 3, 4, 5))
old_func()
}
10 changes: 10 additions & 0 deletions crates/lsh/definitions/markdown.lsh
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,16 @@ pub fn markdown() {
if /.*/ {}
}
}
} else if /(?i:odin)/ {
loop {
await input;
if /\s*```/ {
return;
} else {
odin();
if /.*/ {}
}
}
Comment thread
santerijps marked this conversation as resolved.
} else if /(?i:py)/ {
loop {
await input;
Expand Down
66 changes: 66 additions & 0 deletions crates/lsh/definitions/odin.lsh
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
#[display_name = "Odin"]
#[path = "**/*.odin"]
pub fn odin() {
until /$/ {
yield other;

if /\/\/.*/ {
yield comment;
} else if /\/\*/ {
loop {
yield comment;
if /\*\// { yield comment; break; }
await input;
}
} else if /"/ {
until /$/ {
yield string;
if /\\./ {}
else if /"/ { yield string; break; }
await input;
}
} else if /`/ {
loop {
yield string;
if /`/ { yield string; break; }
await input;
}
} else if /'/ {
until /$/ {
yield string;
if /\\./ {}
else if /'/ { yield string; break; }
await input;
}
} else if /(?:break|case|continue|do|else|fallthrough|for|if|in|not_in|or_break|or_continue|or_else|or_return|return|switch|when|where)\>/ {
yield keyword.control;
} else if /(?:align_of|auto_cast|bit_field|bit_set|cast|context|defer|distinct|dynamic|enum|foreign|import|map|matrix|offset_of|package|proc|size_of|struct|transmute|type_of|typeid|union|using)\>/ {
yield keyword.other;
} else if /(?:any|b8|b16|b32|b64|bool|complex32|complex64|complex128|cstring|f16|f16be|f16le|f32|f32be|f32le|f64|f64be|f64le|i8|i16|i16be|i16le|i32|i32be|i32le|i64|i64be|i64le|i128|i128be|i128le|int|quaternion64|quaternion128|quaternion256|rawptr|rune|string|u8|u16|u16be|u16le|u32|u32be|u32le|u64|u64be|u64le|u128|u128be|u128le|uint|uintptr)\>/ {
yield keyword.other;
} else if /(?:true|false|nil)\>/ {
yield constant.language;
} else if /#\+\w+/ {
yield keyword.other;
if /(?:.+)\>/ {
yield string;
}
} else if /#\w+/ {
yield keyword.other;
} else if /@/ {
Comment thread
santerijps marked this conversation as resolved.
yield markup.link;
} else if /(?i:-?(?:0x[\da-fA-F_]+|0b[01_]+|0o[0-7_]+|[\d_]+\.?[\d_]*|\.[\d_]+)(?:e[+-]?[\d_]+)?i?)/ {
if /\w+/ {
// Invalid numeric literal
} else {
yield constant.numeric;
}
} else if /(\w+)\s*\(/ {
yield $1 as method;
} else if /\w+/ {
// Gobble word chars to align the next iteration on a word boundary.
}

yield other;
}
}
Loading