diff options
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/base64/base64.tm | 26 | ||||
| -rw-r--r-- | examples/colorful/colorful.tm | 202 | ||||
| -rw-r--r-- | examples/commands/commands.tm | 78 | ||||
| -rw-r--r-- | examples/game/box.tm | 4 | ||||
| -rw-r--r-- | examples/game/game.tm | 4 | ||||
| -rw-r--r-- | examples/game/player.tm | 6 | ||||
| -rw-r--r-- | examples/game/raylib.tm | 36 | ||||
| -rw-r--r-- | examples/game/world.tm | 50 | ||||
| -rw-r--r-- | examples/http-server/connection-queue.tm | 10 | ||||
| -rw-r--r-- | examples/http-server/http-server.tm | 66 | ||||
| -rwxr-xr-x | examples/http-server/sample-site/random.tm | 2 | ||||
| -rw-r--r-- | examples/http/http.tm | 36 | ||||
| -rw-r--r-- | examples/ini/ini.tm | 16 | ||||
| -rw-r--r-- | examples/learnxiny.tm | 60 | ||||
| -rw-r--r-- | examples/log/log.tm | 24 | ||||
| -rw-r--r-- | examples/patterns/patterns.tm | 32 | ||||
| -rw-r--r-- | examples/pthreads/pthreads.tm | 66 | ||||
| -rw-r--r-- | examples/random/random.tm | 48 | ||||
| -rw-r--r-- | examples/shell/shell.tm | 40 | ||||
| -rw-r--r-- | examples/time/time.tm | 46 | ||||
| -rw-r--r-- | examples/tomo-install/tomo-install.tm | 26 | ||||
| -rw-r--r-- | examples/tomodeps/tomodeps.tm | 70 | ||||
| -rw-r--r-- | examples/vectors/vectors.tm | 108 | ||||
| -rw-r--r-- | examples/wrap/wrap.tm | 54 |
24 files changed, 555 insertions, 555 deletions
diff --git a/examples/base64/base64.tm b/examples/base64/base64.tm index 714e223b..bf512a83 100644 --- a/examples/base64/base64.tm +++ b/examples/base64/base64.tm @@ -23,15 +23,15 @@ _dec : [Byte] = [ 49, 50, 51, 255, 255, 255, 255, 255, ] -lang Base64: - func parse(text:Text -> Base64?): +lang Base64 + func parse(text:Text -> Base64?) return Base64.from_bytes(text.bytes()) - func from_bytes(bytes:[Byte] -> Base64?): + func from_bytes(bytes:[Byte] -> Base64?) output := &[Byte(0) for _ in bytes.length * 4 / 3 + 4] src := Int64(1) dest := Int64(1) - while src + 2 <= Int64(bytes.length): + while src + 2 <= Int64(bytes.length) chunk24 := ( (Int32(bytes[src]) <<< 16) or (Int32(bytes[src+1]) <<< 8) or Int32(bytes[src+2]) ) @@ -43,7 +43,7 @@ lang Base64: output[dest+3] = _enc[1 + (chunk24 and 0b111111)] dest += 4 - if src + 1 == bytes.length: + if src + 1 == bytes.length chunk16 := ( (Int32(bytes[src]) <<< 8) or Int32(bytes[src+1]) ) @@ -51,7 +51,7 @@ lang Base64: output[dest+1] = _enc[1 + ((chunk16 >>> 4) and 0b111111)] output[dest+2] = _enc[1 + ((chunk16 <<< 2)and 0b111111)] output[dest+3] = _EQUAL_BYTE - else if src == bytes.length: + else if src == bytes.length chunk8 := Int32(bytes[src]) output[dest] = _enc[1 + ((chunk8 >>> 2) and 0b111111)] output[dest+1] = _enc[1 + ((chunk8 <<< 4) and 0b111111)] @@ -60,15 +60,15 @@ lang Base64: return Base64.from_text(Text.from_bytes(output[]) or return none) - func decode_text(b64:Base64 -> Text?): + func decode_text(b64:Base64 -> Text?) return Text.from_bytes(b64.decode_bytes() or return none) - func decode_bytes(b64:Base64 -> [Byte]?): + func decode_bytes(b64:Base64 -> [Byte]?) bytes := b64.text.bytes() output := &[Byte(0) for _ in bytes.length/4 * 3] src := Int64(1) dest := Int64(1) - while src + 3 <= Int64(bytes.length): + while src + 3 <= Int64(bytes.length) chunk24 := ( (Int32(_dec[1+bytes[src]]) <<< 18) or (Int32(_dec[1+bytes[src+1]]) <<< 12) or @@ -82,15 +82,15 @@ lang Base64: output[dest+2] = Byte(chunk24 and 0xFF) dest += 3 - while output[-1] == 0xFF: + while output[-1] == 0xFF output[] = output.to(-2) return output[] -func main(input=(/dev/stdin), decode=no): - if decode: +func main(input=(/dev/stdin), decode=no) + if decode b := Base64.from_text(input.read()!) say(b.decode_text()!) - else: + else text := input.read()! say(Base64.parse(text)!.text) diff --git a/examples/colorful/colorful.tm b/examples/colorful/colorful.tm index 6769841d..d64afe6e 100644 --- a/examples/colorful/colorful.tm +++ b/examples/colorful/colorful.tm @@ -9,128 +9,128 @@ CSI := "$\033[" use patterns -lang Colorful: - convert(text:Text -> Colorful): +lang Colorful + convert(text:Text -> Colorful) text = text.translate({"@"="@(at)", "("="@(lparen)", ")"="@(rparen)"}) return Colorful.from_text(text) - convert(i:Int -> Colorful): return Colorful.from_text("$i") - convert(n:Num -> Colorful): return Colorful.from_text("$n") + convert(i:Int -> Colorful) return Colorful.from_text("$i") + convert(n:Num -> Colorful) return Colorful.from_text("$n") - func for_terminal(c:Colorful -> Text): + func for_terminal(c:Colorful -> Text) return CSI ++ "m" ++ _for_terminal(c, _TermState()) - func print(c:Colorful, newline=yes): + func print(c:Colorful, newline=yes) say(c.for_terminal(), newline=newline) -func main(texts:[Text], files:[Path]=[], by_line=no): - for i,text in texts: +func main(texts:[Text], files:[Path]=[], by_line=no) + for i,text in texts colorful := Colorful.from_text(text) colorful.print(newline=no) - if i == texts.length: say("") - else: say(" ", newline=no) + if i == texts.length say("") + else say(" ", newline=no) - if texts.length == 0 and files.length == 0: + if texts.length == 0 and files.length == 0 files = [(/dev/stdin)] - for file in files: - if by_line: - for line in file.by_line() or exit("Could not read file: $file"): + for file in files + if by_line + for line in file.by_line() or exit("Could not read file: $file") colorful := Colorful.from_text(line) colorful.print() - else: + else colorful := Colorful.from_text(file.read() or exit("Could not read file: $file")) colorful.print(newline=no) -func _for_terminal(c:Colorful, state:_TermState -> Text): - return c.text.map_pattern(recursive=no, $Pat/@(?)/, func(m:PatternMatch): _add_ansi_sequences(m.captures[1], state)) +func _for_terminal(c:Colorful, state:_TermState -> Text) + return c.text.map_pattern(recursive=no, $Pat/@(?)/, func(m:PatternMatch) _add_ansi_sequences(m.captures[1], state)) -enum _Color(Default, Bright(color:Int16), Color8Bit(color:Int16), Color24Bit(color:Int32)): - func from_text(text:Text -> _Color?): - if text.matches_pattern($Pat/#{3-6 hex}/): +enum _Color(Default, Bright(color:Int16), Color8Bit(color:Int16), Color24Bit(color:Int32)) + func from_text(text:Text -> _Color?) + if text.matches_pattern($Pat/#{3-6 hex}/) hex := text.from(2) return none unless hex.length == 3 or hex.length == 6 - if hex.length == 3: + if hex.length == 3 hex = hex[1]++hex[1]++hex[2]++hex[2]++hex[3]++hex[3] n := Int32.parse("0x" ++ hex) or return none return Color24Bit(n) - else if text.matches_pattern($Pat/{1-3 digit}/): + else if text.matches_pattern($Pat/{1-3 digit}/) n := Int16.parse(text) or return none - if n >= 0 and n <= 255: return Color8Bit(n) - else if text == "black": return _Color.Color8Bit(0) - else if text == "red": return _Color.Color8Bit(1) - else if text == "green": return _Color.Color8Bit(2) - else if text == "yellow": return _Color.Color8Bit(3) - else if text == "blue": return _Color.Color8Bit(4) - else if text == "magenta": return _Color.Color8Bit(5) - else if text == "cyan": return _Color.Color8Bit(6) - else if text == "white": return _Color.Color8Bit(7) - else if text == "default": return _Color.Default - else if text == "BLACK": return _Color.Bright(0) - else if text == "RED": return _Color.Bright(1) - else if text == "GREEN": return _Color.Bright(2) - else if text == "YELLOW": return _Color.Bright(3) - else if text == "BLUE": return _Color.Bright(4) - else if text == "MAGENTA": return _Color.Bright(5) - else if text == "CYAN": return _Color.Bright(6) - else if text == "WHITE": return _Color.Bright(7) + if n >= 0 and n <= 255 return Color8Bit(n) + else if text == "black" return _Color.Color8Bit(0) + else if text == "red" return _Color.Color8Bit(1) + else if text == "green" return _Color.Color8Bit(2) + else if text == "yellow" return _Color.Color8Bit(3) + else if text == "blue" return _Color.Color8Bit(4) + else if text == "magenta" return _Color.Color8Bit(5) + else if text == "cyan" return _Color.Color8Bit(6) + else if text == "white" return _Color.Color8Bit(7) + else if text == "default" return _Color.Default + else if text == "BLACK" return _Color.Bright(0) + else if text == "RED" return _Color.Bright(1) + else if text == "GREEN" return _Color.Bright(2) + else if text == "YELLOW" return _Color.Bright(3) + else if text == "BLUE" return _Color.Bright(4) + else if text == "MAGENTA" return _Color.Bright(5) + else if text == "CYAN" return _Color.Bright(6) + else if text == "WHITE" return _Color.Bright(7) return none - func fg(c:_Color -> Text): - when c is Color8Bit(color): - if color >= 0 and color <= 7: return "$(30+color)" - else if color >= 0 and color <= 255: return "38;5;$color" - is Color24Bit(hex): - if hex >= 0 and hex <= 0xFFFFFF: + func fg(c:_Color -> Text) + when c is Color8Bit(color) + if color >= 0 and color <= 7 return "$(30+color)" + else if color >= 0 and color <= 255 return "38;5;$color" + is Color24Bit(hex) + if hex >= 0 and hex <= 0xFFFFFF return "38;2;$((hex >> 16) and 0xFF);$((hex >> 8) and 0xFF);$((hex >> 0) and 0xFF)" - is Bright(color): - if color <= 7: return "$(90+color)" - is Default: + is Bright(color) + if color <= 7 return "$(90+color)" + is Default return "39" fail("Invalid foreground color: '$c'") - func bg(c:_Color -> Text): - when c is Color8Bit(color): - if color >= 0 and color <= 7: return "$(40+color)" - else if color >= 0 and color <= 255: return "48;5;$color" - is Color24Bit(hex): - if hex >= 0 and hex <= 0xFFFFFF: + func bg(c:_Color -> Text) + when c is Color8Bit(color) + if color >= 0 and color <= 7 return "$(40+color)" + else if color >= 0 and color <= 255 return "48;5;$color" + is Color24Bit(hex) + if hex >= 0 and hex <= 0xFFFFFF return "48;2;$((hex >> 16) and 0xFF);$((hex >> 8) and 0xFF);$((hex >> 0) and 0xFF)" - is Bright(color): - if color <= 7: return "$(90+color)" - is Default: + is Bright(color) + if color <= 7 return "$(90+color)" + is Default return "49" fail("Invalid background color: '$c'") - func underline(c:_Color -> Text): - when c is Color8Bit(color): - if color >= 0 and color <= 255: return "58;5;$color" - is Color24Bit(hex): - if hex >= 0 and hex <= 0xFFFFFF: + func underline(c:_Color -> Text) + when c is Color8Bit(color) + if color >= 0 and color <= 255 return "58;5;$color" + is Color24Bit(hex) + if hex >= 0 and hex <= 0xFFFFFF return "58;2;$((hex >> 16) and 0xFF);$((hex >> 8) and 0xFF);$((hex >> 0) and 0xFF)" - is Default: + is Default return "59" - is Bright(color): + is Bright(color) pass fail("Invalid underline color: '$c'") -func _toggle(sequences:&[Text], cur,new:Bool, apply,unapply:Text; inline): - if new and not cur: +func _toggle(sequences:&[Text], cur,new:Bool, apply,unapply:Text; inline) + if new and not cur sequences.insert(apply) - else if cur and not new: + else if cur and not new sequences.insert(unapply) -func _toggle2(sequences:&[Text], cur1,cur2,new1,new2:Bool, apply1,apply2,unapply:Text; inline): +func _toggle2(sequences:&[Text], cur1,cur2,new1,new2:Bool, apply1,apply2,unapply:Text; inline) return if new1 == cur1 and new2 == cur2 - if (cur1 and not new1) or (cur2 and not new2): # Gotta wipe at least one + if (cur1 and not new1) or (cur2 and not new2) # Gotta wipe at least one sequences.insert(unapply) cur1, cur2 = no, no # Wiped out - if new1 and not cur1: + if new1 and not cur1 sequences.insert(apply1) - if new2 and not cur2: + if new2 and not cur2 sequences.insert(apply2) struct _TermState( @@ -138,9 +138,9 @@ struct _TermState( reverse=no, conceal=no, strikethrough=no, fraktur=no, frame=no, encircle=no, overline=no, superscript=no, subscript=no, bg=_Color.Default, fg=_Color.Default, underline_color=_Color.Default, -): +) - func apply(old,new:_TermState -> Text): + func apply(old,new:_TermState -> Text) sequences : &[Text] = &[] _toggle2(sequences, old.bold, old.dim, new.bold, new.dim, "1", "2", "22") _toggle2(sequences, old.italic, old.fraktur, new.italic, new.fraktur, "3", "20", "23") @@ -153,66 +153,66 @@ struct _TermState( _toggle(sequences, old.overline, new.overline, "53", "55") _toggle2(sequences, old.subscript, old.subscript, new.superscript, new.superscript, "73", "74", "75") - if new.bg != old.bg: + if new.bg != old.bg sequences.insert(new.bg.bg()) - if new.fg != old.fg: + if new.fg != old.fg sequences.insert(new.fg.fg()) - if new.underline_color != old.underline_color: + if new.underline_color != old.underline_color sequences.insert(new.underline_color.underline()) - if sequences.length == 0: + if sequences.length == 0 return "" return CSI ++ ";".join(sequences) ++ "m" -func _add_ansi_sequences(text:Text, prev_state:_TermState -> Text): - if text == "lparen": return "(" - else if text == "rparen": return ")" - else if text == "@" or text == "at": return "@" +func _add_ansi_sequences(text:Text, prev_state:_TermState -> Text) + if text == "lparen" return "(" + else if text == "rparen" return ")" + else if text == "@" or text == "at" return "@" parts := ( text.pattern_captures($Pat/{0+..}:{0+..}/) or return "@("++_for_terminal(Colorful.from_text(text), prev_state)++")" ) attributes := parts[1].split_pattern($Pat/{0+space},{0+space}/) new_state := prev_state - for attr in attributes: - if attr.starts_with("fg="): + for attr in attributes + if attr.starts_with("fg=") new_state.fg = _Color.from_text(attr.from(4))! - else if attr.starts_with("bg="): + else if attr.starts_with("bg=") new_state.bg = _Color.from_text(attr.from(4))! - else if attr.starts_with("ul="): + else if attr.starts_with("ul=") new_state.underline_color = _Color.from_text(attr.from(4))! - else if color := _Color.from_text(attr): + else if color := _Color.from_text(attr) new_state.fg = color - else if attr == "b" or attr == "bold": + else if attr == "b" or attr == "bold" new_state.bold = yes - else if attr == "d" or attr == "dim": + else if attr == "d" or attr == "dim" new_state.dim = yes - else if attr == "i" or attr == "italic": + else if attr == "i" or attr == "italic" new_state.italic = yes - else if attr == "u" or attr == "underline": + else if attr == "u" or attr == "underline" new_state.underline = yes - else if attr == "B" or attr == "blink": + else if attr == "B" or attr == "blink" new_state.blink = yes - else if attr == "r" or attr == "reverse": + else if attr == "r" or attr == "reverse" new_state.reverse = yes - else if attr == "fraktur": + else if attr == "fraktur" new_state.fraktur = yes - else if attr == "frame": + else if attr == "frame" new_state.frame = yes - else if attr == "encircle": + else if attr == "encircle" new_state.encircle = yes - else if attr == "overline": + else if attr == "overline" new_state.overline = yes - else if attr == "super" or attr == "superscript": + else if attr == "super" or attr == "superscript" new_state.superscript = yes - else if attr == "sub" or attr == "subscript": + else if attr == "sub" or attr == "subscript" new_state.subscript = yes - else: + else fail("Invalid attribute: '$attr'") result := prev_state.apply(new_state) - result ++= parts[2].map_pattern(recursive=no, $Pat/@(?)/, func(m:PatternMatch): _add_ansi_sequences(m.captures[1], new_state)) + result ++= parts[2].map_pattern(recursive=no, $Pat/@(?)/, func(m:PatternMatch) _add_ansi_sequences(m.captures[1], new_state)) result ++= new_state.apply(prev_state) return result diff --git a/examples/commands/commands.tm b/examples/commands/commands.tm index 6817fca1..2dd5aaf7 100644 --- a/examples/commands/commands.tm +++ b/examples/commands/commands.tm @@ -6,86 +6,86 @@ use -lunistring extern run_command : func(exe:Text, args:[Text], env:{Text=Text}, input:[Byte]?, output:&[Byte]?, error:&[Byte]? -> Int32) extern command_by_line : func(exe:Text, args:[Text], env:{Text=Text} -> func(->Text?)?) -enum ExitType(Exited(status:Int32), Signaled(signal:Int32), Failed): - func succeeded(e:ExitType -> Bool): - when e is Exited(status): return (status == 0) - else: return no +enum ExitType(Exited(status:Int32), Signaled(signal:Int32), Failed) + func succeeded(e:ExitType -> Bool) + when e is Exited(status) return (status == 0) + else return no - func or_fail(e:ExitType, message:Text?=none): - if not e.succeeded(): + func or_fail(e:ExitType, message:Text?=none) + if not e.succeeded() fail(message or "Program failed: $e") -struct ProgramResult(stdout:[Byte], stderr:[Byte], exit_type:ExitType): - func or_fail(r:ProgramResult, message:Text?=none -> ProgramResult): - when r.exit_type is Exited(status): - if status == 0: +struct ProgramResult(stdout:[Byte], stderr:[Byte], exit_type:ExitType) + func or_fail(r:ProgramResult, message:Text?=none -> ProgramResult) + when r.exit_type is Exited(status) + if status == 0 return r - else: fail(message or "Program failed: $r") + else fail(message or "Program failed: $r") fail(message or "Program failed: $r") - func output_text(r:ProgramResult, trim_newline=yes -> Text?): - when r.exit_type is Exited(status): - if status == 0: - if text := Text.from_bytes(r.stdout): - if trim_newline: + func output_text(r:ProgramResult, trim_newline=yes -> Text?) + when r.exit_type is Exited(status) + if status == 0 + if text := Text.from_bytes(r.stdout) + if trim_newline text = text.without_suffix(\n) return text - else: return none + else return none return none - func error_text(r:ProgramResult -> Text?): - when r.exit_type is Exited(status): - if status == 0: + func error_text(r:ProgramResult -> Text?) + when r.exit_type is Exited(status) + if status == 0 return Text.from_bytes(r.stderr) - else: return none + else return none return none - func succeeded(r:ProgramResult -> Bool): - when r.exit_type is Exited(status): + func succeeded(r:ProgramResult -> Bool) + when r.exit_type is Exited(status) return (status == 0) - else: + else return no -struct Command(command:Text, args:[Text]=[], env:{Text=Text}={}): - func from_path(path:Path, args:[Text]=[], env:{Text=Text}={} -> Command): +struct Command(command:Text, args:[Text]=[], env:{Text=Text}={}) + func from_path(path:Path, args:[Text]=[], env:{Text=Text}={} -> Command) return Command(Text(path), args, env) - func result(command:Command, input="", input_bytes:[Byte]=[] -> ProgramResult): - if input.length > 0: + func result(command:Command, input="", input_bytes:[Byte]=[] -> ProgramResult) + if input.length > 0 (&input_bytes).insert_all(input.bytes()) stdout : [Byte] = [] stderr : [Byte] = [] status := run_command(command.command, command.args, command.env, input_bytes, &stdout, &stderr) - if inline C : Bool { WIFEXITED(_$status) }: + if inline C : Bool { WIFEXITED(_$status) } return ProgramResult(stdout, stderr, ExitType.Exited(inline C : Int32 { WEXITSTATUS(_$status) })) - if inline C : Bool { WIFSIGNALED(_$status) }: + if inline C : Bool { WIFSIGNALED(_$status) } return ProgramResult(stdout, stderr, ExitType.Signaled(inline C : Int32 { WTERMSIG(_$status) })) return ProgramResult(stdout, stderr, ExitType.Failed) - func run(command:Command, -> ExitType): + func run(command:Command, -> ExitType) status := run_command(command.command, command.args, command.env, none, none, none) - if inline C : Bool { WIFEXITED(_$status) }: + if inline C : Bool { WIFEXITED(_$status) } return ExitType.Exited(inline C : Int32 { WEXITSTATUS(_$status) }) - if inline C : Bool { WIFSIGNALED(_$status) }: + if inline C : Bool { WIFSIGNALED(_$status) } return ExitType.Signaled(inline C : Int32 { WTERMSIG(_$status) }) return ExitType.Failed - func get_output(command:Command, input="", trim_newline=yes -> Text?): + func get_output(command:Command, input="", trim_newline=yes -> Text?) return command.result(input=input).output_text(trim_newline=trim_newline) - func get_output_bytes(command:Command, input="", input_bytes:[Byte]=[] -> [Byte]?): + func get_output_bytes(command:Command, input="", input_bytes:[Byte]=[] -> [Byte]?) result := command.result(input=input, input_bytes=input_bytes) - when result.exit_type is Exited(status): - if status == 0: return result.stdout + when result.exit_type is Exited(status) + if status == 0 return result.stdout return none - else: return none + else return none - func by_line(command:Command -> func(->Text?)?): + func by_line(command:Command -> func(->Text?)?) return command_by_line(command.command, command.args, command.env) diff --git a/examples/game/box.tm b/examples/game/box.tm index dda3dd83..41ae10e5 100644 --- a/examples/game/box.tm +++ b/examples/game/box.tm @@ -2,6 +2,6 @@ use ./world.tm use ./raylib.tm -struct Box(pos:Vector2, size=Vector2(50, 50), color=Color(0x80,0x80,0x80)): - func draw(b:Box): +struct Box(pos:Vector2, size=Vector2(50, 50), color=Color(0x80,0x80,0x80)) + func draw(b:Box) DrawRectangleV(b.pos, b.size, b.color) diff --git a/examples/game/game.tm b/examples/game/game.tm index ce08c329..f82e4f40 100644 --- a/examples/game/game.tm +++ b/examples/game/game.tm @@ -2,7 +2,7 @@ use ./raylib.tm use ./world.tm -func main(map=(./map.txt)): +func main(map=(./map.txt)) InitWindow(1600, 900, CString("raylib [core] example - 2d camera")) map_contents := map.read() or exit("Could not find the game map: $map") @@ -16,7 +16,7 @@ func main(map=(./map.txt)): SetTargetFPS(60) - while not WindowShouldClose(): + while not WindowShouldClose() dt := GetFrameTime() world.update(dt) diff --git a/examples/game/player.tm b/examples/game/player.tm index 7f14f51e..9f166d9e 100644 --- a/examples/game/player.tm +++ b/examples/game/player.tm @@ -2,14 +2,14 @@ use ./world.tm use ./raylib.tm -struct Player(pos,prev_pos:Vector2): +struct Player(pos,prev_pos:Vector2) WALK_SPEED := Num32(500.) ACCEL := Num32(0.3) FRICTION := Num32(0.99) SIZE := Vector2(30, 30) COLOR := Color(0x60, 0x60, 0xbF) - func update(p:@Player): + func update(p:@Player) target_x := inline C:Num32 { (Num32_t)((IsKeyDown(KEY_A) ? -1 : 0) + (IsKeyDown(KEY_D) ? 1 : 0)) } @@ -24,5 +24,5 @@ struct Player(pos,prev_pos:Vector2): p.prev_pos, p.pos = p.pos, p.pos + World.DT*vel - func draw(p:Player): + func draw(p:Player) DrawRectangleV(p.pos, Player.SIZE, Player.COLOR) diff --git a/examples/game/raylib.tm b/examples/game/raylib.tm index 5e58e996..b2ba53d7 100644 --- a/examples/game/raylib.tm +++ b/examples/game/raylib.tm @@ -4,44 +4,44 @@ use <raylib.h> use <raymath.h> struct Color(r,g,b:Byte,a=Byte(255); extern) -struct Rectangle(x,y,width,height:Num32; extern): - func draw(r:Rectangle, color:Color): +struct Rectangle(x,y,width,height:Num32; extern) + func draw(r:Rectangle, color:Color) DrawRectangleRec(r, color) -struct Vector2(x,y:Num32; extern): +struct Vector2(x,y:Num32; extern) ZERO := Vector2(0, 0) - func plus(a,b:Vector2->Vector2; inline): + func plus(a,b:Vector2->Vector2; inline) return Vector2(a.x+b.x, a.y+b.y) - func minus(a,b:Vector2->Vector2; inline): + func minus(a,b:Vector2->Vector2; inline) return Vector2(a.x-b.x, a.y-b.y) - func times(a,b:Vector2->Vector2; inline): + func times(a,b:Vector2->Vector2; inline) return Vector2(a.x*b.x, a.y*b.y) - func negative(v:Vector2->Vector2; inline): + func negative(v:Vector2->Vector2; inline) return Vector2(-v.x, -v.y) - func dot(a,b:Vector2->Num32; inline): + func dot(a,b:Vector2->Num32; inline) return ((a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y)) - func cross(a,b:Vector2->Num32; inline): + func cross(a,b:Vector2->Num32; inline) return a.x*b.y - a.y*b.x - func scaled_by(v:Vector2, k:Num32->Vector2; inline): + func scaled_by(v:Vector2, k:Num32->Vector2; inline) return Vector2(v.x*k, v.y*k) - func divided_by(v:Vector2, divisor:Num32->Vector2; inline): + func divided_by(v:Vector2, divisor:Num32->Vector2; inline) return Vector2(v.x/divisor, v.y/divisor) - func length(v:Vector2->Num32; inline): + func length(v:Vector2->Num32; inline) return (v.x*v.x + v.y*v.y).sqrt() - func dist(a,b:Vector2->Num32; inline): + func dist(a,b:Vector2->Num32; inline) return a.minus(b).length() - func angle(v:Vector2->Num32; inline): + func angle(v:Vector2->Num32; inline) return Num32.atan2(v.y, v.x) - func norm(v:Vector2->Vector2; inline): - if v.x == 0 and v.y == 0: + func norm(v:Vector2->Vector2; inline) + if v.x == 0 and v.y == 0 return v len := v.length() return Vector2(v.x/len, v.y/len) - func rotated(v:Vector2, radians:Num32 -> Vector2): + func rotated(v:Vector2, radians:Num32 -> Vector2) cos := radians.cos() or return v sin := radians.sin() or return v return Vector2(cos*v.x - sin*v.y, sin*v.x + cos*v.y) - func mix(a,b:Vector2, amount:Num32 -> Vector2): + func mix(a,b:Vector2, amount:Num32 -> Vector2) return Vector2( amount.mix(a.x, b.x), amount.mix(a.y, b.y), diff --git a/examples/game/world.tm b/examples/game/world.tm index e8255ab8..0de8ea4b 100644 --- a/examples/game/world.tm +++ b/examples/game/world.tm @@ -5,7 +5,7 @@ use ./raylib.tm use ./box.tm # Return a displacement relative to `a` that will push it out of `b` -func solve_overlap(a_pos:Vector2, a_size:Vector2, b_pos:Vector2, b_size:Vector2 -> Vector2): +func solve_overlap(a_pos:Vector2, a_size:Vector2, b_pos:Vector2, b_size:Vector2 -> Vector2) a_left := a_pos.x a_right := a_pos.x + a_size.x a_top := a_pos.y @@ -21,68 +21,68 @@ func solve_overlap(a_pos:Vector2, a_size:Vector2, b_pos:Vector2, b_size:Vector2 overlap_y := (a_bottom _min_ b_bottom) - (a_top _max_ b_top) # If either axis is not overlapping, then there is no collision: - if overlap_x <= 0 or overlap_y <= 0: + if overlap_x <= 0 or overlap_y <= 0 return Vector2(0, 0) - if overlap_x < overlap_y: - if a_right > b_left and a_right < b_right: + if overlap_x < overlap_y + if a_right > b_left and a_right < b_right return Vector2(-(overlap_x), 0) - else if a_left < b_right and a_left > b_left: + else if a_left < b_right and a_left > b_left return Vector2(overlap_x, 0) - else: - if a_top < b_bottom and a_top > b_top: + else + if a_top < b_bottom and a_top > b_top return Vector2(0, overlap_y) - else if a_bottom > b_top and a_bottom < b_bottom: + else if a_bottom > b_top and a_bottom < b_bottom return Vector2(0, -overlap_y) return Vector2(0, 0) -struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=Num32(0.0), won=no): +struct World(player:@Player, goal:@Box, boxes:@[@Box], dt_accum=Num32(0.0), won=no) DT := (Num32(1.)/Num32(60.)) STIFFNESS := Num32(0.3) - func update(w:@World, dt:Num32): + func update(w:@World, dt:Num32) w.dt_accum += dt - while w.dt_accum > 0: + while w.dt_accum > 0 w.update_once() w.dt_accum -= World.DT - func update_once(w:@World): + func update_once(w:@World) w.player.update() - if solve_overlap(w.player.pos, Player.SIZE, w.goal.pos, w.goal.size) != Vector2(0,0): + if solve_overlap(w.player.pos, Player.SIZE, w.goal.pos, w.goal.size) != Vector2(0,0) w.won = yes # Resolve player overlapping with any boxes: - for i in 3: - for b in w.boxes: + for i in 3 + for b in w.boxes w.player.pos += World.STIFFNESS * solve_overlap(w.player.pos, Player.SIZE, b.pos, b.size) - func draw(w:@World): - for b in w.boxes: + func draw(w:@World) + for b in w.boxes b.draw() w.goal.draw() w.player.draw() - if w.won: + if w.won DrawText(CString("WINNER"), GetScreenWidth()/Int32(2)-Int32(48*3), GetScreenHeight()/Int32(2)-Int32(24), 48, Color(0,0,0)) - func load_map(w:@World, map:Text): - if map.has("[]"): + func load_map(w:@World, map:Text) + if map.has("[]") map = map.translate({"[]"="#", "@ "="@", " "=" "}) w.boxes = @[] box_size := Vector2(50., 50.) - for y,line in map.lines(): - for x,cell in line.split(): - if cell == "#": + for y,line in map.lines() + for x,cell in line.split() + if cell == "#" pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) box := @Box(pos, size=box_size, color=Color(0x80,0x80,0x80)) w.boxes.insert(box) - else if cell == "@": + else if cell == "@" pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) pos += box_size/Num32(2) - Player.SIZE/Num32(2) w.player = @Player(pos,pos) - else if cell == "?": + else if cell == "?" pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y) w.goal.pos = pos diff --git a/examples/http-server/connection-queue.tm b/examples/http-server/connection-queue.tm index 3c8058d5..29759f38 100644 --- a/examples/http-server/connection-queue.tm +++ b/examples/http-server/connection-queue.tm @@ -1,22 +1,22 @@ use pthreads -func _assert_success(name:Text, val:Int32; inline): +func _assert_success(name:Text, val:Int32; inline) fail("$name() failed!") if val < 0 -struct ConnectionQueue(_connections:@[Int32]=@[], _mutex=pthread_mutex_t.new(), _cond=pthread_cond_t.new()): - func enqueue(queue:ConnectionQueue, connection:Int32): +struct ConnectionQueue(_connections:@[Int32]=@[], _mutex=pthread_mutex_t.new(), _cond=pthread_cond_t.new()) + func enqueue(queue:ConnectionQueue, connection:Int32) queue._mutex.lock() queue._connections.insert(connection) queue._mutex.unlock() queue._cond.signal() - func dequeue(queue:ConnectionQueue -> Int32): + func dequeue(queue:ConnectionQueue -> Int32) conn : Int32? = none queue._mutex.lock() - while queue._connections.length == 0: + while queue._connections.length == 0 queue._cond.wait(queue._mutex) conn = queue._connections.pop(1) diff --git a/examples/http-server/http-server.tm b/examples/http-server/http-server.tm index 649a9e12..b814cdcb 100644 --- a/examples/http-server/http-server.tm +++ b/examples/http-server/http-server.tm @@ -15,12 +15,12 @@ use patterns use ./connection-queue.tm -func serve(port:Int32, handler:func(request:HTTPRequest -> HTTPResponse), num_threads=16): +func serve(port:Int32, handler:func(request:HTTPRequest -> HTTPResponse), num_threads=16) connections := ConnectionQueue() workers : &[@pthread_t] = &[] - for i in num_threads: - workers.insert(pthread_t.new(func(): - repeat: + for i in num_threads + workers.insert(pthread_t.new(func() + repeat connection := connections.dequeue() request_text := inline C : Text { Text_t request = EMPTY_TEXT; @@ -62,17 +62,17 @@ func serve(port:Int32, handler:func(request:HTTPRequest -> HTTPResponse), num_th s } - repeat: + repeat conn := inline C : Int32 { accept(_$sock, NULL, NULL) } stop if conn < 0 connections.enqueue(conn) say("Shutting down...") - for w in workers: + for w in workers w.cancel() -struct HTTPRequest(method:Text, path:Text, version:Text, headers:[Text], body:Text): - func from_text(text:Text -> HTTPRequest?): +struct HTTPRequest(method:Text, path:Text, version:Text, headers:[Text], body:Text) + func from_text(text:Text -> HTTPRequest?) m := text.pattern_captures($Pat'{word} {..} HTTP/{..}{crlf}{..}') or return none method := m[1] path := m[2].replace_pattern($Pat'{2+ /}', '/') @@ -82,8 +82,8 @@ struct HTTPRequest(method:Text, path:Text, version:Text, headers:[Text], body:Te body := rest[-1] return HTTPRequest(method, path, version, headers, body) -struct HTTPResponse(body:Text, status=200, content_type="text/plain", headers:{Text=Text}={}): - func bytes(r:HTTPResponse -> [Byte]): +struct HTTPResponse(body:Text, status=200, content_type="text/plain", headers:{Text=Text}={}) + func bytes(r:HTTPResponse -> [Byte]) body_bytes := r.body.bytes() extra_headers := (++: "$k: $v$(\r\n)" for k,v in r.headers) or "" return " @@ -95,55 +95,55 @@ struct HTTPResponse(body:Text, status=200, content_type="text/plain", headers:{T $\r$\n ".bytes() ++ body_bytes -func _content_type(file:Path -> Text): - when file.extension() is "html": return "text/html" - is "tm": return "text/html" - is "js": return "text/javascript" - is "css": return "text/css" - else: return "text/plain" - -enum RouteEntry(ServeFile(file:Path), Redirect(destination:Text)): - func respond(entry:RouteEntry, request:HTTPRequest -> HTTPResponse): - when entry is ServeFile(file): - body := if file.can_execute(): +func _content_type(file:Path -> Text) + when file.extension() is "html" return "text/html" + is "tm" return "text/html" + is "js" return "text/javascript" + is "css" return "text/css" + else return "text/plain" + +enum RouteEntry(ServeFile(file:Path), Redirect(destination:Text)) + func respond(entry:RouteEntry, request:HTTPRequest -> HTTPResponse) + when entry is ServeFile(file) + body := if file.can_execute() Command(Text(file)).get_output()! - else: + else file.read()! return HTTPResponse(body, content_type=_content_type(file)) - is Redirect(destination): + is Redirect(destination) return HTTPResponse("Found", 302, headers={"Location"=destination}) -func load_routes(directory:Path -> {Text=RouteEntry}): +func load_routes(directory:Path -> {Text=RouteEntry}) routes : &{Text=RouteEntry} = &{} - for file in (directory ++ (./*)).glob(): + for file in (directory ++ (./*)).glob() skip unless file.is_file() contents := file.read() or skip server_path := "/" ++ "/".join(file.relative_to(directory).components) - if file.base_name() == "index.html": + if file.base_name() == "index.html" canonical := server_path.without_suffix("index.html") routes[server_path] = Redirect(canonical) routes[canonical] = ServeFile(file) - else if file.extension() == "html": + else if file.extension() == "html" canonical := server_path.without_suffix(".html") routes[server_path] = Redirect(canonical) routes[canonical] = ServeFile(file) - else if file.extension() == "tm": + else if file.extension() == "tm" canonical := server_path.without_suffix(".tm") routes[server_path] = Redirect(canonical) routes[canonical] = ServeFile(file) - else: + else routes[server_path] = ServeFile(file) return routes[] -func main(directory:Path, port=Int32(8080)): +func main(directory:Path, port=Int32(8080)) say("Serving on port $port") routes := load_routes(directory) say(" Hosting: $routes") - serve(port, func(request:HTTPRequest): - if handler := routes[request.path]: + serve(port, func(request:HTTPRequest) + if handler := routes[request.path] return handler.respond(request) - else: + else return HTTPResponse("Not found!", 404) ) diff --git a/examples/http-server/sample-site/random.tm b/examples/http-server/sample-site/random.tm index 29b93be7..153ac2af 100755 --- a/examples/http-server/sample-site/random.tm +++ b/examples/http-server/sample-site/random.tm @@ -1,7 +1,7 @@ #!/bin/env tomo use random -func main(): +func main() say(" <!DOCTYPE HTML> <html> diff --git a/examples/http/http.tm b/examples/http/http.tm index 048ffaab..39cbfd6d 100644 --- a/examples/http/http.tm +++ b/examples/http/http.tm @@ -7,9 +7,9 @@ struct HTTPResponse(code:Int, body:Text) enum _Method(GET, POST, PUT, PATCH, DELETE) -func _send(method:_Method, url:Text, data:Text?, headers:[Text]=[] -> HTTPResponse): +func _send(method:_Method, url:Text, data:Text?, headers:[Text]=[] -> HTTPResponse) chunks : @[Text] = @[] - save_chunk := func(chunk:CString, size:Int64, n:Int64): + save_chunk := func(chunk:CString, size:Int64, n:Int64) chunks.insert(inline C:Text { Text$format("%.*s", _$size*_$n, _$chunk) }) @@ -23,45 +23,45 @@ func _send(method:_Method, url:Text, data:Text?, headers:[Text]=[] -> HTTPRespon curl_easy_setopt(curl, CURLOPT_WRITEDATA, _$save_chunk.userdata); } - defer: + defer inline C { if (chunk) curl_slist_free_all(chunk); curl_easy_cleanup(curl); } - when method is POST: + when method is POST inline C { curl_easy_setopt(curl, CURLOPT_POST, 1L); } - if posting := data: + if posting := data inline C { curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Text$as_c_string(_$posting)); } - is PUT: + is PUT inline C { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT"); } - if putting := data: + if putting := data inline C { curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Text$as_c_string(_$putting)); } - is PATCH: + is PATCH inline C { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH"); } - if patching := data: + if patching := data inline C { curl_easy_setopt(curl, CURLOPT_POSTFIELDS, Text$as_c_string(_$patching)); } - is DELETE: + is DELETE inline C { curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE"); } - else: + else pass - for header in headers: + for header in headers inline C { chunk = curl_slist_append(chunk, Text$as_c_string(_$header)); } @@ -81,22 +81,22 @@ func _send(method:_Method, url:Text, data:Text?, headers:[Text]=[] -> HTTPRespon } return HTTPResponse(Int(code), "".join(chunks)) -func get(url:Text, headers:[Text]=[] -> HTTPResponse): +func get(url:Text, headers:[Text]=[] -> HTTPResponse) return _send(GET, url, none, headers) -func post(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse): +func post(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse) return _send(POST, url, data, headers) -func put(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse): +func put(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse) return _send(PUT, url, data, headers) -func patch(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse): +func patch(url:Text, data="", headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse) return _send(PATCH, url, data, headers) -func delete(url:Text, data:Text?=none, headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse): +func delete(url:Text, data:Text?=none, headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse) return _send(DELETE, url, data, headers) -func main(): +func main() say("GET:") say(get("https://httpbin.org/get").body) say("Waiting 1sec...") diff --git a/examples/ini/ini.tm b/examples/ini/ini.tm index 3fa4c834..7d53d7e5 100644 --- a/examples/ini/ini.tm +++ b/examples/ini/ini.tm @@ -9,7 +9,7 @@ _HELP := " $_USAGE " -func parse_ini(path:Path -> {Text={Text=Text}}): +func parse_ini(path:Path -> {Text={Text=Text}}) text := path.read() or exit("Could not read INI file: $\[31;1]$(path)$\[]") sections : @{Text=@{Text=Text}} = @{} current_section : @{Text=Text} = @{} @@ -17,30 +17,30 @@ func parse_ini(path:Path -> {Text={Text=Text}}): # Line wraps: text = text.replace_pattern($Pat/\{1 nl}{0+space}/, " ") - for line in text.lines(): + for line in text.lines() line = line.trim() skip if line.starts_with(";") or line.starts_with("#") - if line.matches_pattern($Pat/[?]/): + if line.matches_pattern($Pat/[?]/) section_name := line.replace($Pat/[?]/, "\1").trim().lower() current_section = @{} sections[section_name] = current_section - else if line.matches_pattern($Pat/{..}={..}/): + else if line.matches_pattern($Pat/{..}={..}/) key := line.replace_pattern($Pat/{..}={..}/, "\1").trim().lower() value := line.replace_pattern($Pat/{..}={..}/, "\2").trim() current_section[key] = value return {k=v[] for k,v in sections[]} -func main(path:Path, key:Text?): +func main(path:Path, key:Text?) keys := (key or "").split($|/|) - if keys.length > 2: + if keys.length > 2 exit(" Too many arguments! $_USAGE ") data := parse_ini(path) - if keys.length < 1 or keys[1] == '*': + if keys.length < 1 or keys[1] == '*' say("$data") return @@ -49,7 +49,7 @@ func main(path:Path, key:Text?): Invalid section name: $\[31;1]$section$\[] Valid names: $\[1]$(", ".join([k.quoted() for k in data.keys]))$\[] ") - if keys.length < 2 or keys[2] == '*': + if keys.length < 2 or keys[2] == '*' say("$section_data") return diff --git a/examples/learnxiny.tm b/examples/learnxiny.tm index 74cba5df..5bf8e69f 100644 --- a/examples/learnxiny.tm +++ b/examples/learnxiny.tm @@ -3,7 +3,7 @@ # which is compiled to a binary using your C compiler of choice. # To begin with, let's define a main function: -func main(): +func main() # This function's code will run if you run this file. # Print to the console @@ -45,11 +45,11 @@ func main(): my_bool := yes # Conditionals: - if my_bool: + if my_bool say("It worked!") - else if my_variable == 99: + else if my_variable == 99 say("else if") - else: + else say("else") # Arrays: @@ -73,11 +73,11 @@ func main(): # exit. # Iteration: - for num in my_numbers: + for num in my_numbers >> num # Optionally, you can use an iteration index as well: - for index, num in my_numbers: + for index, num in my_numbers pass # Pass means "do nothing" # Arrays can be created with array comprehensions, which are loops: @@ -87,15 +87,15 @@ func main(): = [100, 300] # Loop control flow uses "skip" and "stop" - for x in my_numbers: - for y in my_numbers: - if x == y: + for x in my_numbers + for y in my_numbers + if x == y skip # For readability, you can also use postfix conditionals: skip if x == y - if x + y == 60: + if x + y == 60 # Skip or stop can specify a loop variable if you want to # affect an enclosing loop: stop x @@ -123,10 +123,10 @@ func main(): empty_table : {Text=Int} = {} # Tables can be iterated over either by key or key,value: - for key in table: + for key in table pass - for key, value in table: + for key, value in table pass # Tables also have ".keys" and ".values" fields to explicitly access the @@ -213,15 +213,15 @@ func main(): # Functions must be declared at the top level of a file and must specify the # types of all of their arguments and return value (if any): -func add(x:Int, y:Int -> Int): +func add(x:Int, y:Int -> Int) return x + y # Default values for arguments can be provided in place of a type (the type is # inferred from the default value): -func show_both(first:Int, second=0 -> Text): +func show_both(first:Int, second=0 -> Text) return "first=$first second=$second" -func demo_keyword_args(): +func demo_keyword_args() >> show_both(1, 2) = "first=1 second=2" @@ -245,27 +245,27 @@ func takes_many_types( pointer_to_mutable_array_of_ints:@[Int], optional_int:Int?, function_from_int_to_text:func(x:Int -> Text), -): +) pass # Now let's define our own datastructure, a humble struct: -struct Person(name:Text, age:Int): +struct Person(name:Text, age:Int) # We can define constants here if we want to: max_age := 122 # Methods are defined here as well: - func say_age(self:Person): + func say_age(self:Person) say("My age is $self.age") # If you want to mutate a value, you must have a mutable pointer: - func increase_age(self:@Person, amount=1): + func increase_age(self:@Person, amount=1) self.age += amount # Methods don't have to take a Person as their first argument: - func get_cool_name(->Text): + func get_cool_name(->Text) return "Blade" -func demo_structs(): +func demo_structs() # Creating a struct: alice := Person("Alice", 30) >> alice @@ -304,23 +304,23 @@ enum Shape( Point, Circle(radius:Num), Rectangle(width:Num, height:Num), -): +) # Just like with structs, you define methods and constants inside a level # of indentation: - func get_area(self:Shape->Num): + func get_area(self:Shape->Num) # In order to work with an enum, it's most often handy to use a 'when' # statement to get the internal values: - when self is Point: + when self is Point return 0 - is Circle(r): + is Circle(r) return Num.PI * r^2 - is Rectangle(w, h): + is Rectangle(w, h) return w * h # 'when' statements are checked for exhaustiveness, so the compiler # will give an error if you forgot any cases. You can also use 'else:' # if you want a fallback to handle other cases. -func demo_enums(): +func demo_enums() # Enums are constructed like this: my_shape := Shape.Circle(1.0) @@ -339,16 +339,16 @@ func demo_enums(): >> {my_shape="nice"} = {Shape.Circle(1)="nice"} -func demo_lambdas(): +func demo_lambdas() # Lambdas, or anonymous functions, can be used like this: - add_one := func(x:Int): x + 1 + add_one := func(x:Int) x + 1 >> add_one(5) = 6 # Lambdas can capture closure values, but only as a snapshot from when the # lambda was created: n := 10 - add_n := func(x:Int): x + n + add_n := func(x:Int) x + n >> add_n(5) = 15 diff --git a/examples/log/log.tm b/examples/log/log.tm index 047398b0..3763303f 100644 --- a/examples/log/log.tm +++ b/examples/log/log.tm @@ -5,7 +5,7 @@ timestamp_format := CString("%F %T") logfiles : @|Path| = @|| -func _timestamp(->Text): +func _timestamp(->Text) c_str := inline C:CString { char *str = GC_MALLOC_ATOMIC(20); time_t t; time(&t); @@ -15,33 +15,33 @@ func _timestamp(->Text): } return c_str.as_text() -func info(text:Text, newline=yes): +func info(text:Text, newline=yes) say("$\[2]⚫ $text$\[]", newline) - for file in logfiles: + for file in logfiles file.append("$(_timestamp()) [info] $text$\n") -func debug(text:Text, newline=yes): +func debug(text:Text, newline=yes) say("$\[32]🟢 $text$\[]", newline) - for file in logfiles: + for file in logfiles file.append("$(_timestamp()) [debug] $text$\n") -func warn(text:Text, newline=yes): +func warn(text:Text, newline=yes) say("$\[33;1]🟡 $text$\[]", newline) - for file in logfiles: + for file in logfiles file.append("$(_timestamp()) [warn] $text$\n") -func error(text:Text, newline=yes): +func error(text:Text, newline=yes) say("$\[31;1]🔴 $text$\[]", newline) - for file in logfiles: + for file in logfiles file.append("$(_timestamp()) [error] $text$\n") -func add_logfile(file:Path): +func add_logfile(file:Path) logfiles.add(file) -func remove_logfile(file:Path): +func remove_logfile(file:Path) logfiles.remove(file) -func main(): +func main() add_logfile((./log.txt)) >> info("Hello") >> debug("Hello") diff --git a/examples/patterns/patterns.tm b/examples/patterns/patterns.tm index 15226848..3e671b80 100644 --- a/examples/patterns/patterns.tm +++ b/examples/patterns/patterns.tm @@ -2,46 +2,46 @@ use ./patterns.c struct PatternMatch(text:Text, index:Int, captures:[Text]) -lang Pat: - convert(text:Text -> Pat): +lang Pat + convert(text:Text -> Pat) return inline C : Pat { Pattern$escape_text(_$text); } - convert(n:Int -> Pat): + convert(n:Int -> Pat) return Pat.from_text("$n") -extend Text: - func matches_pattern(text:Text, pattern:Pat -> Bool): +extend Text + func matches_pattern(text:Text, pattern:Pat -> Bool) return inline C : Bool { Pattern$matches(_$text, _$pattern); } - func pattern_captures(text:Text, pattern:Pat -> [Text]?): + func pattern_captures(text:Text, pattern:Pat -> [Text]?) return inline C : [Text]? { Pattern$captures(_$text, _$pattern); } - func replace_pattern(text:Text, pattern:Pat, replacement:Text, backref="@", recursive=yes -> Text): + func replace_pattern(text:Text, pattern:Pat, replacement:Text, backref="@", recursive=yes -> Text) return inline C : Text { Pattern$replace(_$text, _$pattern, _$replacement, _$backref, _$recursive); } - func translate_patterns(text:Text, replacements:{Pat=Text}, backref="@", recursive=yes -> Text): + func translate_patterns(text:Text, replacements:{Pat=Text}, backref="@", recursive=yes -> Text) return inline C : Text { Pattern$replace_all(_$text, _$replacements, _$backref, _$recursive); } - func has_pattern(text:Text, pattern:Pat -> Bool): + func has_pattern(text:Text, pattern:Pat -> Bool) return inline C : Bool { Pattern$has(_$text, _$pattern); } - func find_patterns(text:Text, pattern:Pat -> [PatternMatch]): + func find_patterns(text:Text, pattern:Pat -> [PatternMatch]) return inline C : [PatternMatch] { Pattern$find_all(_$text, _$pattern); } - func by_pattern(text:Text, pattern:Pat -> func(->PatternMatch?)): + func by_pattern(text:Text, pattern:Pat -> func(->PatternMatch?)) return inline C : func(->PatternMatch?) { Pattern$by_match(_$text, _$pattern); } - func each_pattern(text:Text, pattern:Pat, fn:func(m:PatternMatch), recursive=yes): + func each_pattern(text:Text, pattern:Pat, fn:func(m:PatternMatch), recursive=yes) inline C { Pattern$each(_$text, _$pattern, _$fn, _$recursive); } - func map_pattern(text:Text, pattern:Pat, fn:func(m:PatternMatch -> Text), recursive=yes -> Text): + func map_pattern(text:Text, pattern:Pat, fn:func(m:PatternMatch -> Text), recursive=yes -> Text) return inline C : Text { Pattern$map(_$text, _$pattern, _$fn, _$recursive); } - func split_pattern(text:Text, pattern:Pat -> [Text]): + func split_pattern(text:Text, pattern:Pat -> [Text]) return inline C : [Text] { Pattern$split(_$text, _$pattern); } - func by_pattern_split(text:Text, pattern:Pat -> func(->Text?)): + func by_pattern_split(text:Text, pattern:Pat -> func(->Text?)) return inline C : func(->Text?) { Pattern$by_split(_$text, _$pattern); } - func trim_pattern(text:Text, pattern=$Pat"{space}", left=yes, right=yes -> Text): + func trim_pattern(text:Text, pattern=$Pat"{space}", left=yes, right=yes -> Text) return inline C : Text { Pattern$trim(_$text, _$pattern, _$left, _$right); } diff --git a/examples/pthreads/pthreads.tm b/examples/pthreads/pthreads.tm index 3be052b4..61f94d0a 100644 --- a/examples/pthreads/pthreads.tm +++ b/examples/pthreads/pthreads.tm @@ -1,8 +1,8 @@ # A Posix Threads (pthreads) wrapper use <pthread.h> -struct pthread_mutex_t(; extern, opaque): - func new(->@pthread_mutex_t): +struct pthread_mutex_t(; extern, opaque) + func new(->@pthread_mutex_t) return inline C : @pthread_mutex_t { pthread_mutex_t *mutex = GC_MALLOC(sizeof(pthread_mutex_t)); pthread_mutex_init(mutex, NULL); @@ -10,14 +10,14 @@ struct pthread_mutex_t(; extern, opaque): mutex } - func lock(m:&pthread_mutex_t): + func lock(m:&pthread_mutex_t) fail("Failed to lock mutex") unless inline C : Int32 { pthread_mutex_lock(_$m); } == 0 - func unlock(m:&pthread_mutex_t): + func unlock(m:&pthread_mutex_t) fail("Failed to unlock mutex") unless inline C : Int32 { pthread_mutex_unlock(_$m); } == 0 -struct pthread_cond_t(; extern, opaque): - func new(->@pthread_cond_t): +struct pthread_cond_t(; extern, opaque) + func new(->@pthread_cond_t) return inline C : @pthread_cond_t { pthread_cond_t *cond = GC_MALLOC(sizeof(pthread_cond_t)); pthread_cond_init(cond, NULL); @@ -25,17 +25,17 @@ struct pthread_cond_t(; extern, opaque): cond } - func wait(cond:&pthread_cond_t, mutex:&pthread_mutex_t): + func wait(cond:&pthread_cond_t, mutex:&pthread_mutex_t) fail("Failed to wait on condition") unless inline C : Int32 { pthread_cond_wait(_$cond, _$mutex); } == 0 - func signal(cond:&pthread_cond_t): + func signal(cond:&pthread_cond_t) fail("Failed to signal pthread_cond_t") unless inline C : Int32 { pthread_cond_signal(_$cond); } == 0 - func broadcast(cond:&pthread_cond_t): + func broadcast(cond:&pthread_cond_t) fail("Failed to broadcast pthread_cond_t") unless inline C : Int32 { pthread_cond_broadcast(_$cond); } == 0 -struct pthread_rwlock_t(; extern, opaque): - func new(->@pthread_rwlock_t): +struct pthread_rwlock_t(; extern, opaque) + func new(->@pthread_rwlock_t) return inline C : @pthread_rwlock_t { pthread_rwlock_t *lock = GC_MALLOC(sizeof(pthread_rwlock_t)); pthread_rwlock_init(lock, NULL); @@ -43,60 +43,60 @@ struct pthread_rwlock_t(; extern, opaque): lock } - func read_lock(lock:&pthread_rwlock_t): + func read_lock(lock:&pthread_rwlock_t) inline C { pthread_rwlock_rdlock(_$lock); } - func write_lock(lock:&pthread_rwlock_t): + func write_lock(lock:&pthread_rwlock_t) inline C { pthread_rwlock_wrlock(_$lock); } - func unlock(lock:&pthread_rwlock_t): + func unlock(lock:&pthread_rwlock_t) inline C { pthread_rwlock_unlock(_$lock); } -struct pthread_t(; extern, opaque): - func new(fn:func() -> @pthread_t): +struct pthread_t(; extern, opaque) + func new(fn:func() -> @pthread_t) return inline C : @pthread_t { pthread_t *thread = GC_MALLOC(sizeof(pthread_t)); pthread_create(thread, NULL, _$fn.fn, _$fn.userdata); thread } - func join(p:pthread_t): inline C { pthread_join(_$p, NULL); } - func cancel(p:pthread_t): inline C { pthread_cancel(_$p); } - func detatch(p:pthread_t): inline C { pthread_detach(_$p); } + func join(p:pthread_t) inline C { pthread_join(_$p, NULL); } + func cancel(p:pthread_t) inline C { pthread_cancel(_$p); } + func detatch(p:pthread_t) inline C { pthread_detach(_$p); } -struct IntQueue(_queue:@[Int], _mutex:@pthread_mutex_t, _cond:@pthread_cond_t): - func new(initial:[Int]=[] -> IntQueue): +struct IntQueue(_queue:@[Int], _mutex:@pthread_mutex_t, _cond:@pthread_cond_t) + func new(initial:[Int]=[] -> IntQueue) return IntQueue(@initial, pthread_mutex_t.new(), pthread_cond_t.new()) - func give(q:IntQueue, n:Int): - do: q._mutex.lock() + func give(q:IntQueue, n:Int) + do q._mutex.lock() q._queue.insert(n) q._mutex.unlock() q._cond.signal() - func take(q:IntQueue -> Int): - do: q._mutex.lock() + func take(q:IntQueue -> Int) + do q._mutex.lock() n := q._queue.pop(1) - while not n: + while not n q._cond.wait(q._mutex) n = q._queue.pop(1) q._mutex.unlock() return n! fail("Unreachable") -func main(): +func main() jobs := IntQueue.new() results := IntQueue.new() say_mutex := pthread_mutex_t.new() - announce := func(speaker:Text, text:Text): - do: say_mutex.lock() + announce := func(speaker:Text, text:Text) + do say_mutex.lock() say("$\033[2m[$speaker]$\033[m $text") say_mutex.unlock() - worker := pthread_t.new(func(): + worker := pthread_t.new(func() say("I'm in the thread!") - repeat: + repeat announce("worker", "waiting for job") job := jobs.take() result := job * 10 @@ -105,12 +105,12 @@ func main(): announce("worker", "Signaled $result") ) - for i in 10: + for i in 10 announce("boss", "Pushing job $i") jobs.give(i) announce("boss", "Gave job $i") - for i in 10: + for i in 10 announce("boss", "Getting result...") result := results.take() announce("boss", "Got result $result") diff --git a/examples/random/random.tm b/examples/random/random.tm index 1f60aff0..94528dd7 100644 --- a/examples/random/random.tm +++ b/examples/random/random.tm @@ -3,8 +3,8 @@ use ./sysrandom.h use ./chacha.h -struct chacha_ctx(j0,j1,j2,j3,j4,j5,j6,j7,j8,j9,j10,j11,j12,j13,j14,j15:Int32; extern, secret): - func from_seed(seed:[Byte]=[] -> chacha_ctx): +struct chacha_ctx(j0,j1,j2,j3,j4,j5,j6,j7,j8,j9,j10,j11,j12,j13,j14,j15:Int32; extern, secret) + func from_seed(seed:[Byte]=[] -> chacha_ctx) return inline C : chacha_ctx { chacha_ctx ctx; uint8_t seed_bytes[KEYSZ + IVSZ] = {}; @@ -17,19 +17,19 @@ struct chacha_ctx(j0,j1,j2,j3,j4,j5,j6,j7,j8,j9,j10,j11,j12,j13,j14,j15:Int32; e random := RandomNumberGenerator.new() -func _os_random_bytes(count:Int64 -> [Byte]): +func _os_random_bytes(count:Int64 -> [Byte]) return inline C : [Byte] { uint8_t *random_bytes = GC_MALLOC_ATOMIC(_$count); getrandom(random_bytes, _$count, 0); (Array_t){.length=_$count, .data=random_bytes, .stride=1, .atomic=1}; } -struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret): - func new(seed:[Byte]?=none, -> @RandomNumberGenerator): +struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret) + func new(seed:[Byte]?=none, -> @RandomNumberGenerator) ctx := chacha_ctx.from_seed(seed or _os_random_bytes(40)) return @RandomNumberGenerator(ctx, []) - func _rekey(rng:&RandomNumberGenerator): + func _rekey(rng:&RandomNumberGenerator) rng._random_bytes = inline C : [Byte] { Byte_t new_keystream[KEYSZ + IVSZ] = {}; // Fill the buffer with the keystream @@ -43,7 +43,7 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret new_bytes; } - func _fill_bytes(rng:&RandomNumberGenerator, dest:&Memory, needed:Int64): + func _fill_bytes(rng:&RandomNumberGenerator, dest:&Memory, needed:Int64) inline C { while (_$needed > 0) { if (_$rng->_random_bytes.length == 0) @@ -62,7 +62,7 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret } } - func bytes(rng:&RandomNumberGenerator, count:Int -> [Byte]): + func bytes(rng:&RandomNumberGenerator, count:Int -> [Byte]) return inline C : [Byte] { int64_t count64 = Int64$from_int(_$count, false); Array_t ret = {.data=GC_MALLOC_ATOMIC(count64), .stride=1, .atomic=1, .length=count64}; @@ -70,23 +70,23 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret ret; } - func byte(rng:&RandomNumberGenerator -> Byte): + func byte(rng:&RandomNumberGenerator -> Byte) return inline C : Byte { Byte_t b; _$random$RandomNumberGenerator$_fill_bytes(_$rng, &b, sizeof(b)); b; } - func bool(rng:&RandomNumberGenerator, probability=0.5 -> Bool): - if probability == 0.5: + func bool(rng:&RandomNumberGenerator, probability=0.5 -> Bool) + if probability == 0.5 return rng.byte() < 0x80 - else: + else return rng.num(0., 1.) < 0.5 - func int64(rng:&RandomNumberGenerator, min=Int64.min, max=Int64.max -> Int64): + func int64(rng:&RandomNumberGenerator, min=Int64.min, max=Int64.max -> Int64) fail("Random minimum value $min is larger than the maximum value $max") if min > max return min if min == max - if min == Int64.min and max == Int64.max: + if min == Int64.min and max == Int64.max return inline C : Int64 { int64_t i; _$random$RandomNumberGenerator$_fill_bytes(_$rng, &i, sizeof(i)); @@ -104,10 +104,10 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret (int64_t)((uint64_t)_$min + (r % range)); } - func int32(rng:&RandomNumberGenerator, min=Int32.min, max=Int32.max -> Int32): + func int32(rng:&RandomNumberGenerator, min=Int32.min, max=Int32.max -> Int32) fail("Random minimum value $min is larger than the maximum value $max") if min > max return min if min == max - if min == Int32.min and max == Int32.max: + if min == Int32.min and max == Int32.max return inline C : Int32 { int32_t i; _$random$RandomNumberGenerator$_fill_bytes(_$rng, &i, sizeof(i)); @@ -125,10 +125,10 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret (int32_t)((uint32_t)_$min + (r % range)); } - func int16(rng:&RandomNumberGenerator, min=Int16.min, max=Int16.max -> Int16): + func int16(rng:&RandomNumberGenerator, min=Int16.min, max=Int16.max -> Int16) fail("Random minimum value $min is larger than the maximum value $max") if min > max return min if min == max - if min == Int16.min and max == Int16.max: + if min == Int16.min and max == Int16.max return inline C : Int16 { int16_t i; _$random$RandomNumberGenerator$_fill_bytes(_$rng, &i, sizeof(i)); @@ -146,10 +146,10 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret (int16_t)((uint16_t)_$min + (r % range)); } - func int8(rng:&RandomNumberGenerator, min=Int8.min, max=Int8.max -> Int8): + func int8(rng:&RandomNumberGenerator, min=Int8.min, max=Int8.max -> Int8) fail("Random minimum value $min is larger than the maximum value $max") if min > max return min if min == max - if min == Int8.min and max == Int8.max: + if min == Int8.min and max == Int8.max return inline C : Int8 { int8_t i; _$random$RandomNumberGenerator$_fill_bytes(_$rng, &i, sizeof(i)); @@ -167,7 +167,7 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret (int8_t)((uint8_t)_$min + (r % range)); } - func num(rng:&RandomNumberGenerator, min=0., max=1. -> Num): + func num(rng:&RandomNumberGenerator, min=0., max=1. -> Num) return inline C : Num { if (_$min > _$max) fail("Random minimum value (", _$min, ") is larger than the maximum value (", _$max, ")"); if (_$min == _$max) return _$min; @@ -187,10 +187,10 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret (_$min == 0.0 && _$max == 1.0) ? r.num : ((1.0-r.num)*_$min + r.num*_$max); } - func num32(rng:&RandomNumberGenerator, min=Num32(0.), max=Num32(1.) -> Num32): + func num32(rng:&RandomNumberGenerator, min=Num32(0.), max=Num32(1.) -> Num32) return Num32(rng.num(Num(min), Num(max))) - func int(rng:&RandomNumberGenerator, min:Int, max:Int -> Int): + func int(rng:&RandomNumberGenerator, min:Int, max:Int -> Int) return inline C : Int { if (likely(((_$min.small & _$max.small) & 1) != 0)) { int32_t r = _$random$RandomNumberGenerator$int32(_$rng, (int32_t)(_$min.small >> 2), (int32_t)(_$max.small >> 2)); @@ -226,7 +226,7 @@ struct RandomNumberGenerator(_chacha:chacha_ctx, _random_bytes:[Byte]=[]; secret } -func main(): +func main() >> rng := RandomNumberGenerator.new() >> rng.num() >> rng.num() diff --git a/examples/shell/shell.tm b/examples/shell/shell.tm index 816d63a2..da03f843 100644 --- a/examples/shell/shell.tm +++ b/examples/shell/shell.tm @@ -1,44 +1,44 @@ use commands -lang Shell: - convert(text:Text -> Shell): +lang Shell + convert(text:Text -> Shell) return Shell.from_text("'" ++ text.replace($/'/, `'"'"'`) ++ "'") - convert(texts:[Text] -> Shell): + convert(texts:[Text] -> Shell) return Shell.from_text(" ".join([Shell(t).text for t in texts])) - convert(path:Path -> Shell): + convert(path:Path -> Shell) return Shell(Text(path.expand_home())) - convert(paths:[Path] -> Shell): + convert(paths:[Path] -> Shell) return Shell.from_text(" ".join([Shell(Text(p)).text for p in paths])) - convert(n:Int -> Shell): return Shell.from_text(Text(n)) - convert(n:Int64 -> Shell): return Shell.from_text(Text(n)) - convert(n:Int32 -> Shell): return Shell.from_text(Text(n)) - convert(n:Int16 -> Shell): return Shell.from_text(Text(n)) - convert(n:Int8 -> Shell): return Shell.from_text(Text(n)) - convert(n:Num -> Shell): return Shell.from_text(Text(n)) - convert(n:Num32 -> Shell): return Shell.from_text(Text(n)) + convert(n:Int -> Shell) return Shell.from_text(Text(n)) + convert(n:Int64 -> Shell) return Shell.from_text(Text(n)) + convert(n:Int32 -> Shell) return Shell.from_text(Text(n)) + convert(n:Int16 -> Shell) return Shell.from_text(Text(n)) + convert(n:Int8 -> Shell) return Shell.from_text(Text(n)) + convert(n:Num -> Shell) return Shell.from_text(Text(n)) + convert(n:Num32 -> Shell) return Shell.from_text(Text(n)) - func command(shell:Shell -> Command): + func command(shell:Shell -> Command) return Command("sh", ["-c", shell.text]) - func result(shell:Shell, input="", input_bytes:[Byte]=[] -> ProgramResult): + func result(shell:Shell, input="", input_bytes:[Byte]=[] -> ProgramResult) return shell.command().result(input=input, input_bytes=input_bytes) - func run(shell:Shell -> ExitType): + func run(shell:Shell -> ExitType) return shell.command().run() - func get_output(shell:Shell, input="", trim_newline=yes -> Text?): + func get_output(shell:Shell, input="", trim_newline=yes -> Text?) return shell.command().get_output(input=input, trim_newline=trim_newline) - func get_output_bytes(shell:Shell, input="", input_bytes:[Byte]=[] -> [Byte]?): + func get_output_bytes(shell:Shell, input="", input_bytes:[Byte]=[] -> [Byte]?) return shell.command().get_output_bytes(input=input, input_bytes=input_bytes) - func by_line(shell:Shell -> func(->Text?)?): + func by_line(shell:Shell -> func(->Text?)?) return shell.command().by_line() -func main(command:Shell): - for line in command.by_line()!: +func main(command:Shell) + for line in command.by_line()! >> line diff --git a/examples/time/time.tm b/examples/time/time.tm index 4d686ac7..4a5bfcf7 100644 --- a/examples/time/time.tm +++ b/examples/time/time.tm @@ -5,8 +5,8 @@ enum Weekday(Sunday, Monday, Tuesday, Wednesday, Thursday, Friday, Saturday) struct TimeInfo(year,month,day,hour,minute,second,nanosecond:Int, weekday:Weekday, day_of_year:Int, timezone:Text) -struct Time(tv_sec:Int64, tv_usec:Int64; extern): - func now(->Time): +struct Time(tv_sec:Int64, tv_usec:Int64; extern) + func now(->Time) return inline C : Time { struct timespec ts; if (clock_gettime(CLOCK_REALTIME, &ts) != 0) @@ -14,7 +14,7 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): (Time){.tv_sec=ts.tv_sec, .tv_usec=ts.tv_nsec/1000}; } - func local_timezone(->Text): + func local_timezone(->Text) inline C { if (_local_timezone.length < 0) { static char buf[PATH_MAX]; @@ -31,14 +31,14 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): } return inline C : Text { _local_timezone; } - func set_local_timezone(timezone:Text): + func set_local_timezone(timezone:Text) inline C { setenv("TZ", Text$as_c_string(_$timezone), 1); _local_timezone = _$timezone; tzset(); } - func format(t:Time, format="%c", timezone=Time.local_timezone() -> Text): + func format(t:Time, format="%c", timezone=Time.local_timezone() -> Text) return inline C : Text { struct tm result; time_t time = _$t.tv_sec; @@ -49,7 +49,7 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): Text$from_strn(buf, len); } - func new(year,month,day:Int, hour=0, minute=0, second=0.0, timezone=Time.local_timezone() -> Time): + func new(year,month,day:Int, hour=0, minute=0, second=0.0, timezone=Time.local_timezone() -> Time) return inline C : Time{ struct tm info = { .tm_min=Int32$from_int(_$minute, false), @@ -65,24 +65,24 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): (Time){.tv_sec=t + (time_t)_$second, .tv_usec=(suseconds_t)(fmod(_$second, 1.0) * 1e9)}; } - func unix_timestamp(t:Time -> Int64): + func unix_timestamp(t:Time -> Int64) return inline C : Int64 { (int64_t)_$t.tv_sec } - func from_unix_timestamp(timestamp:Int64 -> Time): + func from_unix_timestamp(timestamp:Int64 -> Time) return inline C : Time { (Time){.tv_sec=_$timestamp}; } - func seconds_till(t:Time, target:Time -> Num): + func seconds_till(t:Time, target:Time -> Num) seconds := Num(target.tv_sec - t.tv_sec) seconds += 1e-9*Num(target.tv_usec - t.tv_usec) return seconds - func minutes_till(t:Time, target:Time -> Num): + func minutes_till(t:Time, target:Time -> Num) return t.seconds_till(target)/60. - func hours_till(t:Time, target:Time -> Num): + func hours_till(t:Time, target:Time -> Num) return t.seconds_till(target)/3600. - func relative(t:Time, relative_to=Time.now(), timezone=Time.local_timezone() -> Text): + func relative(t:Time, relative_to=Time.now(), timezone=Time.local_timezone() -> Text) inline C { struct tm info = {}; struct tm relative_info = {}; @@ -114,21 +114,21 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): } fail("Unreachable") - func time(t:Time, seconds=no, am_pm=yes, timezone=Time.local_timezone() -> Text): - time := if seconds and am_pm: + func time(t:Time, seconds=no, am_pm=yes, timezone=Time.local_timezone() -> Text) + time := if seconds and am_pm t.format("%l:%M:%S%P") - else if seconds and not am_pm: + else if seconds and not am_pm t.format("%T") - else if not seconds and am_pm: + else if not seconds and am_pm t.format("%l:%M%P") - else: + else t.format("%H:%M") return time.trim() - func date(t:Time, timezone=Time.local_timezone() -> Text): + func date(t:Time, timezone=Time.local_timezone() -> Text) return t.format("%F") - func info(t:Time, timezone=Time.local_timezone() -> TimeInfo): + func info(t:Time, timezone=Time.local_timezone() -> TimeInfo) return inline C : TimeInfo { struct tm info = {}; WITH_TIMEZONE(_$timezone, localtime_r(&_$t.tv_sec, &info)); @@ -146,7 +146,7 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): }; } - func after(t:Time, seconds=0.0, minutes=0.0, hours=0.0, days=0, weeks=0, months=0, years=0, timezone=Time.local_timezone() -> Time): + func after(t:Time, seconds=0.0, minutes=0.0, hours=0.0, days=0, weeks=0, months=0, years=0, timezone=Time.local_timezone() -> Time) return inline C : Time { double offset = _$seconds + 60.*_$minutes + 3600.*_$hours ; _$t.tv_sec += (time_t)offset; @@ -165,7 +165,7 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): }; } - func parse(text:Text, format="%Y-%m-%dT%H:%M:%S%z", timezone=Time.local_timezone() -> Time?): + func parse(text:Text, format="%Y-%m-%dT%H:%M:%S%z", timezone=Time.local_timezone() -> Time?) return inline C : Time? { struct tm info = {.tm_isdst=-1}; const char *str = Text$as_c_string(_$text); @@ -184,7 +184,7 @@ struct Time(tv_sec:Int64, tv_usec:Int64; extern): (_$time$$OptionalTime$$type){.value={.tv_sec=t + offset - info.tm_gmtoff}}; } -func _run_tests(): +func _run_tests() >> Time.now().format() >> Time.set_local_timezone("Europe/Paris") >> Time.now().format() @@ -206,5 +206,5 @@ func _run_tests(): # >> Time.parse("2023-11-05 01:01", "%Y-%m-%d %H:%M") # >> Time.parse("2023-11-05 01:01", "%Y-%m-%d %H:%M", timezone="Europe/Paris") -func main(): +func main() _run_tests() diff --git a/examples/tomo-install/tomo-install.tm b/examples/tomo-install/tomo-install.tm index 0a255987..fd8b3c40 100644 --- a/examples/tomo-install/tomo-install.tm +++ b/examples/tomo-install/tomo-install.tm @@ -10,19 +10,19 @@ _HELP := " Usage: $_USAGE " -func find_urls(path:Path -> [Text]): +func find_urls(path:Path -> [Text]) urls : @[Text] = @[] - if path.is_directory(): - for f in path.children(): + if path.is_directory() + for f in path.children() urls.insert_all(find_urls(f)) - else if path.is_file() and path.extension() == ".tm": - for line in path.by_line()!: - if captures := line.pattern_captures($Pat/use{space}{url}/) or line.pattern_captures($Pat/{id}{space}:={space}use{space}{url}/): + else if path.is_file() and path.extension() == ".tm" + for line in path.by_line()! + if captures := line.pattern_captures($Pat/use{space}{url}/) or line.pattern_captures($Pat/{id}{space}:={space}use{space}{url}/) urls.insert(captures[-1]) return urls -func main(paths:[Path]): - if paths.length == 0: +func main(paths:[Path]) + if paths.length == 0 paths = [(./)] urls := (++: find_urls(p) for p in paths) or [] @@ -32,23 +32,23 @@ func main(paths:[Path]): (~/.local/share/tomo/installed).create_directory() (~/.local/share/tomo/lib).create_directory() - for url in urls: + for url in urls original_url := url url_without_protocol := url.trim_pattern($Pat"http{0-1 s}://", right=no) hash := $Shell@(echo -n @url_without_protocol | sha256sum).get_output()!.slice(to=32) - if (~/.local/share/tomo/installed/$hash).is_directory(): + if (~/.local/share/tomo/installed/$hash).is_directory() say("Already installed: $url") skip alias : Text? = none curl_flags := ["-L"] - if github := url_without_protocol.pattern_captures($Pat"github.com/{!/}/{!/}#{..}"): + if github := url_without_protocol.pattern_captures($Pat"github.com/{!/}/{!/}#{..}") user := github[1] repo := github[2] tag := github[3] url = "https://api.github.com/repos/$user/$repo/tarball/$tag" alias = "$(repo.without_prefix("tomo-")).$(tag).$(user)" - if github_token: + if github_token curl_flags ++= ["-H", "Authorization: Bearer $github_token"] curl_flags ++= [ "-H", "Accept: application/vnd.github+json", @@ -69,7 +69,7 @@ func main(paths:[Path]): fi `.get_output()!) - if alias: + if alias say($Shell( set -exuo pipefail ln -f -s @hash ~/.local/share/tomo/installed/@alias diff --git a/examples/tomodeps/tomodeps.tm b/examples/tomodeps/tomodeps.tm index 66bb74bd..1aafa6bd 100644 --- a/examples/tomodeps/tomodeps.tm +++ b/examples/tomodeps/tomodeps.tm @@ -11,107 +11,107 @@ _HELP := " enum Dependency(File(path:Path), Module(name:Text)) -func _get_file_dependencies(file:Path -> |Dependency|): - if not file.is_file(): +func _get_file_dependencies(file:Path -> |Dependency|) + if not file.is_file() say("Could not read file: $file") return || deps : @|Dependency| = @|| - if lines := file.by_line(): - for line in lines: - if line.matches_pattern($Pat/use {..}.tm/): + if lines := file.by_line() + for line in lines + if line.matches_pattern($Pat/use {..}.tm/) file_import := Path.from_text(line.replace_pattern($Pat/use {..}/, "\1")).resolved(relative_to=file) deps.add(Dependency.File(file_import)) - else if line.matches_pattern($Pat/use {id}/): + else if line.matches_pattern($Pat/use {id}/) module_name := line.replace_pattern($Pat/use {..}/, "\1") deps.add(Dependency.Module(module_name)) return deps[] -func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency=|Dependency|}): +func _build_dependency_graph(dep:Dependency, dependencies:@{Dependency=|Dependency|}) return if dependencies.has(dep) dependencies[dep] = || # Placeholder - dep_deps := when dep is File(path): + dep_deps := when dep is File(path) _get_file_dependencies(path) - is Module(module): + is Module(module) dir := (~/.local/share/tomo/installed/$module) module_deps : @|Dependency| = @|| visited : @|Path| = @|| unvisited := @|f.resolved() for f in dir.files() if f.extension() == ".tm"| - while unvisited.length > 0: + while unvisited.length > 0 file := unvisited.items[-1] unvisited.remove(file) visited.add(file) - for file_dep in _get_file_dependencies(file): - when file_dep is File(f): - if not visited.has(f): + for file_dep in _get_file_dependencies(file) + when file_dep is File(f) + if not visited.has(f) unvisited.add(f) - is Module(m): + is Module(m) module_deps.add(file_dep) module_deps[] dependencies[dep] = dep_deps - for dep2 in dep_deps: + for dep2 in dep_deps _build_dependency_graph(dep2, dependencies) -func get_dependency_graph(dep:Dependency -> {Dependency=|Dependency|}): +func get_dependency_graph(dep:Dependency -> {Dependency=|Dependency|}) graph : @{Dependency=|Dependency|} = @{} _build_dependency_graph(dep, graph) return graph -func _printable_name(dep:Dependency -> Text): - when dep is Module(module): +func _printable_name(dep:Dependency -> Text) + when dep is Module(module) return "$(\x1b)[34;1m$module$(\x1b)[m" - is File(f): + is File(f) f = f.relative_to((.)) - if f.exists(): + if f.exists() return Text(f) - else: + else return "$(\x1b)[31;1m$(f) (not found)$(\x1b)[m" -func _draw_tree(dep:Dependency, dependencies:{Dependency=|Dependency|}, already_printed:@|Dependency|, prefix="", is_last=yes): - if already_printed.has(dep): - say(prefix ++ (if is_last: "└── " else: "├── ") ++ _printable_name(dep) ++ " $\x1b[2m(recursive)$\x1b[m") +func _draw_tree(dep:Dependency, dependencies:{Dependency=|Dependency|}, already_printed:@|Dependency|, prefix="", is_last=yes) + if already_printed.has(dep) + say(prefix ++ (if is_last "└── " else "├── ") ++ _printable_name(dep) ++ " $\x1b[2m(recursive)$\x1b[m") return - say(prefix ++ (if is_last: "└── " else: "├── ") ++ _printable_name(dep)) + say(prefix ++ (if is_last "└── " else "├── ") ++ _printable_name(dep)) already_printed.add(dep) - child_prefix := prefix ++ (if is_last: " " else: "│ ") + child_prefix := prefix ++ (if is_last " " else "│ ") children := dependencies[dep] or || - for i,child in children.items: + for i,child in children.items is_child_last := (i == children.length) _draw_tree(child, dependencies, already_printed, child_prefix, is_child_last) -func draw_tree(dep:Dependency, dependencies:{Dependency=|Dependency|}): +func draw_tree(dep:Dependency, dependencies:{Dependency=|Dependency|}) printed : @|Dependency| = @|| say(_printable_name(dep)) printed.add(dep) deps := dependencies[dep] or || - for i,child in deps.items: + for i,child in deps.items is_child_last := (i == deps.length) _draw_tree(child, dependencies, already_printed=printed, is_last=is_child_last) -func main(files:[Text]): - if files.length == 0: +func main(files:[Text]) + if files.length == 0 exit(" Please provide at least one file! $_USAGE ") - for arg in files: - if arg.matches_pattern($Pat/{..}.tm/): + for arg in files + if arg.matches_pattern($Pat/{..}.tm/) path := Path.from_text(arg).resolved() dependencies := get_dependency_graph(File(path)) draw_tree(File(path), dependencies) - else if arg.matches_pattern($Pat/{id}/): + else if arg.matches_pattern($Pat/{id}/) dependencies := get_dependency_graph(Module(arg)) draw_tree(Module(arg), dependencies) - else: + else say("$\x1b[2mSkipping $arg$\x1b[m") skip diff --git a/examples/vectors/vectors.tm b/examples/vectors/vectors.tm index 42ada614..1fd0c4fa 100644 --- a/examples/vectors/vectors.tm +++ b/examples/vectors/vectors.tm @@ -1,72 +1,72 @@ # A math vector library for 2D and 3D vectors of Nums or Ints -struct Vec2(x,y:Num): +struct Vec2(x,y:Num) ZERO := Vec2(0, 0) - func plus(a,b:Vec2->Vec2; inline): + func plus(a,b:Vec2->Vec2; inline) return Vec2(a.x+b.x, a.y+b.y) - func minus(a,b:Vec2->Vec2; inline): + func minus(a,b:Vec2->Vec2; inline) return Vec2(a.x-b.x, a.y-b.y) - func times(a,b:Vec2->Vec2; inline): + func times(a,b:Vec2->Vec2; inline) return Vec2(a.x*b.x, a.y*b.y) - func negative(v:Vec2->Vec2; inline): + func negative(v:Vec2->Vec2; inline) return Vec2(-v.x, -v.y) - func dot(a,b:Vec2->Num; inline): + func dot(a,b:Vec2->Num; inline) return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) - func cross(a,b:Vec2->Num; inline): + func cross(a,b:Vec2->Num; inline) return a.x*b.y - a.y*b.x - func scaled_by(v:Vec2, k:Num->Vec2; inline): + func scaled_by(v:Vec2, k:Num->Vec2; inline) return Vec2(v.x*k, v.y*k) - func divided_by(v:Vec2, divisor:Num->Vec2; inline): + func divided_by(v:Vec2, divisor:Num->Vec2; inline) return Vec2(v.x/divisor, v.y/divisor) - func length(v:Vec2->Num; inline): + func length(v:Vec2->Num; inline) return (v.x*v.x + v.y*v.y).sqrt() - func dist(a,b:Vec2->Num; inline): + func dist(a,b:Vec2->Num; inline) return a.minus(b).length() - func angle(v:Vec2->Num; inline): + func angle(v:Vec2->Num; inline) return Num.atan2(v.y, v.x) - func norm(v:Vec2->Vec2; inline): - if v.x == 0 and v.y == 0: + func norm(v:Vec2->Vec2; inline) + if v.x == 0 and v.y == 0 return v len := v.length() return Vec2(v.x/len, v.y/len) - func rotated(v:Vec2, radians:Num -> Vec2): + func rotated(v:Vec2, radians:Num -> Vec2) cos := radians.cos() or return v sin := radians.sin() or return v return Vec2(cos*v.x - sin*v.y, sin*v.x + cos*v.y) - func mix(a,b:Vec2, amount:Num -> Vec2): + func mix(a,b:Vec2, amount:Num -> Vec2) return Vec2( amount.mix(a.x, b.x), amount.mix(a.y, b.y), ) -struct Vec3(x,y,z:Num): +struct Vec3(x,y,z:Num) ZERO := Vec3(0, 0, 0) - func plus(a,b:Vec3->Vec3; inline): + func plus(a,b:Vec3->Vec3; inline) return Vec3(a.x+b.x, a.y+b.y, a.z+b.z) - func minus(a,b:Vec3->Vec3; inline): + func minus(a,b:Vec3->Vec3; inline) return Vec3(a.x-b.x, a.y-b.y, a.z-b.z) - func times(a,b:Vec3->Vec3; inline): + func times(a,b:Vec3->Vec3; inline) return Vec3(a.x*b.x, a.y*b.y, a.z*b.z) - func negative(v:Vec3->Vec3; inline): + func negative(v:Vec3->Vec3; inline) return Vec3(-v.x, -v.y, -v.z) - func dot(a,b:Vec3->Num; inline): + func dot(a,b:Vec3->Num; inline) return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z) - func cross(a,b:Vec3->Vec3; inline): + func cross(a,b:Vec3->Vec3; inline) return Vec3(a.y*b.z - a.z-b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x) - func scaled_by(v:Vec3, k:Num->Vec3; inline): + func scaled_by(v:Vec3, k:Num->Vec3; inline) return Vec3(v.x*k, v.y*k, v.z*k) - func divided_by(v:Vec3, divisor:Num->Vec3; inline): + func divided_by(v:Vec3, divisor:Num->Vec3; inline) return Vec3(v.x/divisor, v.y/divisor, v.z/divisor) - func length(v:Vec3->Num; inline): + func length(v:Vec3->Num; inline) return (v.x*v.x + v.y*v.y + v.z*v.z).sqrt() - func dist(a,b:Vec3->Num; inline): + func dist(a,b:Vec3->Num; inline) return a.minus(b).length() - func norm(v:Vec3->Vec3; inline): - if v.x == 0 and v.y == 0 and v.z == 0: + func norm(v:Vec3->Vec3; inline) + if v.x == 0 and v.y == 0 and v.z == 0 return v len := v.length() return Vec3(v.x/len, v.y/len, v.z/len) - func mix(a,b:Vec3, amount:Num -> Vec3): + func mix(a,b:Vec3, amount:Num -> Vec3) return Vec3( amount.mix(a.x, b.x), amount.mix(a.y, b.y), @@ -74,60 +74,60 @@ struct Vec3(x,y,z:Num): ) -struct IVec2(x,y:Int): +struct IVec2(x,y:Int) ZERO := IVec2(0, 0) - func plus(a,b:IVec2->IVec2; inline): + func plus(a,b:IVec2->IVec2; inline) return IVec2(a.x+b.x, a.y+b.y) - func minus(a,b:IVec2->IVec2; inline): + func minus(a,b:IVec2->IVec2; inline) return IVec2(a.x-b.x, a.y-b.y) - func times(a,b:IVec2->IVec2; inline): + func times(a,b:IVec2->IVec2; inline) return IVec2(a.x*b.x, a.y*b.y) - func negative(v:IVec2->IVec2; inline): + func negative(v:IVec2->IVec2; inline) return IVec2(-v.x, -v.y) - func dot(a,b:IVec2->Int; inline): + func dot(a,b:IVec2->Int; inline) return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) - func cross(a,b:IVec2->Int; inline): + func cross(a,b:IVec2->Int; inline) return a.x*b.y - a.y*b.x - func scaled_by(v:IVec2, k:Int->IVec2; inline): + func scaled_by(v:IVec2, k:Int->IVec2; inline) return IVec2(v.x*k, v.y*k) - func divided_by(v:IVec2, divisor:Int->IVec2; inline): + func divided_by(v:IVec2, divisor:Int->IVec2; inline) return IVec2(v.x/divisor, v.y/divisor) - func length(v:IVec2->Num; inline): + func length(v:IVec2->Num; inline) x := Num(v.x) y := Num(v.y) return Num.sqrt(x*x + y*y) - func dist(a,b:IVec2->Num; inline): + func dist(a,b:IVec2->Num; inline) return a.minus(b).length() - func angle(v:IVec2->Num; inline): + func angle(v:IVec2->Num; inline) return Num.atan2(Num(v.y), Num(v.x)) -struct IVec3(x,y,z:Int): +struct IVec3(x,y,z:Int) ZERO := IVec3(0, 0, 0) - func plus(a,b:IVec3->IVec3; inline): + func plus(a,b:IVec3->IVec3; inline) return IVec3(a.x+b.x, a.y+b.y, a.z+b.z) - func minus(a,b:IVec3->IVec3; inline): + func minus(a,b:IVec3->IVec3; inline) return IVec3(a.x-b.x, a.y-b.y, a.z-b.z) - func times(a,b:IVec3->IVec3; inline): + func times(a,b:IVec3->IVec3; inline) return IVec3(a.x*b.x, a.y*b.y, a.z*b.z) - func negative(v:IVec3->IVec3; inline): + func negative(v:IVec3->IVec3; inline) return IVec3(-v.x, -v.y, -v.z) - func dot(a,b:IVec3->Int; inline): + func dot(a,b:IVec3->Int; inline) return (a.x-b.x)*(a.x-b.x) + (a.y-b.y)*(a.y-b.y) + (a.z-b.z)*(a.z-b.z) - func cross(a,b:IVec3->IVec3; inline): + func cross(a,b:IVec3->IVec3; inline) return IVec3(a.y*b.z - a.z-b.y, a.z*b.x - a.x*b.z, a.x*b.y - a.y*b.x) - func scaled_by(v:IVec3, k:Int->IVec3; inline): + func scaled_by(v:IVec3, k:Int->IVec3; inline) return IVec3(v.x*k, v.y*k, v.z*k) - func divided_by(v:IVec3, divisor:Int->IVec3; inline): + func divided_by(v:IVec3, divisor:Int->IVec3; inline) return IVec3(v.x/divisor, v.y/divisor, v.z/divisor) - func length(v:IVec3->Num; inline): + func length(v:IVec3->Num; inline) x := Num(v.x) y := Num(v.y) z := Num(v.z) return Num.sqrt(x*x + y*y + z*z) - func dist(a,b:IVec3->Num; inline): + func dist(a,b:IVec3->Num; inline) return a.minus(b).length() -func main(): +func main() >> Vec2(10, 20) >> Vec2(10, 20) + Vec2(100, 100) = Vec2(x=110, y=120) diff --git a/examples/wrap/wrap.tm b/examples/wrap/wrap.tm index 448ab813..c539a0ea 100644 --- a/examples/wrap/wrap.tm +++ b/examples/wrap/wrap.tm @@ -13,19 +13,19 @@ HELP := " UNICODE_HYPHEN := \{hyphen} -func unwrap(text:Text, preserve_paragraphs=yes, hyphen=UNICODE_HYPHEN -> Text): - if preserve_paragraphs: +func unwrap(text:Text, preserve_paragraphs=yes, hyphen=UNICODE_HYPHEN -> Text) + if preserve_paragraphs paragraphs := text.split($/{2+ nl}/) - if paragraphs.length > 1: + if paragraphs.length > 1 return \n\n.join([unwrap(p, hyphen=hyphen, preserve_paragraphs=no) for p in paragraphs]) return text.replace($/$(hyphen)$(\n)/, "") -func wrap(text:Text, width:Int, min_split=3, hyphen="-" -> Text): - if width <= 0: +func wrap(text:Text, width:Int, min_split=3, hyphen="-" -> Text) + if width <= 0 fail("Width must be a positive integer, not $width") - if 2*min_split - hyphen.length > width: + if 2*min_split - hyphen.length > width fail(" Minimum word split length ($min_split) is too small for the given wrap width ($width)! @@ -35,66 +35,66 @@ func wrap(text:Text, width:Int, min_split=3, hyphen="-" -> Text): lines : @[Text] = @[] line := "" - for word in text.split($/{whitespace}/): + for word in text.split($/{whitespace}/) letters := word.split() skip if letters.length == 0 - while not _can_fit_word(line, letters, width): + while not _can_fit_word(line, letters, width) line_space := width - line.length - if line != "": line_space -= 1 + if line != "" line_space -= 1 - if min_split > 0 and line_space >= min_split + hyphen.length and letters.length >= 2*min_split: + if min_split > 0 and line_space >= min_split + hyphen.length and letters.length >= 2*min_split # Split word with a hyphen: split := line_space - hyphen.length split = split _max_ min_split split = split _min_ (letters.length - min_split) - if line != "": line ++= " " + if line != "" line ++= " " line ++= ((++: letters.to(split)) or "") ++ hyphen letters = letters.from(split + 1) - else if line == "": + else if line == "" # Force split word without hyphenation: - if line != "": line ++= " " + if line != "" line ++= " " line ++= (++: letters.to(line_space)) or "" letters = letters.from(line_space + 1) - else: + else pass # Move to next line lines.insert(line) line = "" - if letters.length > 0: - if line != "": line ++= " " + if letters.length > 0 + if line != "" line ++= " " line ++= (++: letters) or "" - if line != "": + if line != "" lines.insert(line) return \n.join(lines) -func _can_fit_word(line:Text, letters:[Text], width:Int -> Bool; inline): - if line == "": +func _can_fit_word(line:Text, letters:[Text], width:Int -> Bool; inline) + if line == "" return letters.length <= width - else: + else return line.length + 1 + letters.length <= width -func main(files:[Path], width=80, inplace=no, min_split=3, rewrap=yes, hyphen=UNICODE_HYPHEN): - if files.length == 0: +func main(files:[Path], width=80, inplace=no, min_split=3, rewrap=yes, hyphen=UNICODE_HYPHEN) + if files.length == 0 files = [(/dev/stdin)] - for file in files: + for file in files text := file.read() or exit("Could not read file: $file") - if rewrap: + if rewrap text = unwrap(text) - out := if file.is_file() and inplace: + out := if file.is_file() and inplace file - else: + else (/dev/stdout) first := yes wrapped_paragraphs : @[Text] = @[] - for paragraph in text.split($/{2+ nl}/): + for paragraph in text.split($/{2+ nl}/) wrapped_paragraphs.insert( wrap(paragraph, width=width, min_split=min_split, hyphen=hyphen) ) |
