diff options
52 files changed, 874 insertions, 883 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 6a3b7e21..d05c92f2 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" @@ -244,27 +244,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 @@ -303,23 +303,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) @@ -338,16 +338,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 7375d5f6..c6c45a21 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 dd7bec10..4ccbec2c 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) ) diff --git a/src/parse.c b/src/parse.c index 0f76243d..ca6a6af2 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1791,7 +1791,6 @@ PARSER(parse_extended_expr) { PARSER(parse_block) { const char *start = pos; spaces(&pos); - if (!match(&pos, ":")) return NULL; ast_list_t *statements = NULL; if (!indent(ctx, &pos)) { @@ -1968,14 +1967,12 @@ PARSER(parse_struct_def) { expect_closing(ctx, &pos, ")", "I wasn't able to parse the rest of this struct"); ast_t *namespace = NULL; - if (match(&pos, ":")) { - const char *ns_pos = pos; - whitespace(&ns_pos); - int64_t ns_indent = get_indent(ctx, ns_pos); - if (ns_indent > starting_indent) { - pos = ns_pos; - namespace = optional(ctx, &pos, parse_namespace); - } + const char *ns_pos = pos; + whitespace(&ns_pos); + int64_t ns_indent = get_indent(ctx, ns_pos); + if (ns_indent > starting_indent) { + pos = ns_pos; + namespace = optional(ctx, &pos, parse_namespace); } if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); @@ -2031,14 +2028,12 @@ PARSER(parse_enum_def) { REVERSE_LIST(tags); ast_t *namespace = NULL; - if (match(&pos, ":")) { - const char *ns_pos = pos; - whitespace(&ns_pos); - int64_t ns_indent = get_indent(ctx, ns_pos); - if (ns_indent > starting_indent) { - pos = ns_pos; - namespace = optional(ctx, &pos, parse_namespace); - } + const char *ns_pos = pos; + whitespace(&ns_pos); + int64_t ns_indent = get_indent(ctx, ns_pos); + if (ns_indent > starting_indent) { + pos = ns_pos; + namespace = optional(ctx, &pos, parse_namespace); } if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); @@ -2058,14 +2053,12 @@ PARSER(parse_lang_def) { spaces(&pos); ast_t *namespace = NULL; - if (match(&pos, ":")) { - const char *ns_pos = pos; - whitespace(&ns_pos); - int64_t ns_indent = get_indent(ctx, ns_pos); - if (ns_indent > starting_indent) { - pos = ns_pos; - namespace = optional(ctx, &pos, parse_namespace); - } + const char *ns_pos = pos; + whitespace(&ns_pos); + int64_t ns_indent = get_indent(ctx, ns_pos); + if (ns_indent > starting_indent) { + pos = ns_pos; + namespace = optional(ctx, &pos, parse_namespace); } if (!namespace) namespace = NewAST(ctx->file, pos, pos, Block, .statements=NULL); @@ -2084,14 +2077,12 @@ PARSER(parse_extend) { parser_err(ctx, start, pos, "I expected a name for this lang"); ast_t *body = NULL; - if (match(&pos, ":")) { - const char *ns_pos = pos; - whitespace(&ns_pos); - int64_t ns_indent = get_indent(ctx, ns_pos); - if (ns_indent > starting_indent) { - pos = ns_pos; - body = optional(ctx, &pos, parse_namespace); - } + const char *ns_pos = pos; + whitespace(&ns_pos); + int64_t ns_indent = get_indent(ctx, ns_pos); + if (ns_indent > starting_indent) { + pos = ns_pos; + body = optional(ctx, &pos, parse_namespace); } if (!body) body = NewAST(ctx->file, pos, pos, Block, .statements=NULL); diff --git a/test/arrays.tm b/test/arrays.tm index c60085ca..f2ed2c7a 100644 --- a/test/arrays.tm +++ b/test/arrays.tm @@ -1,9 +1,9 @@ -func main(): - do: +func main() + do >> nums : [Num32] = [] = [] - do: + do >> arr := [10, 20, 30] = [10, 20, 30] @@ -16,18 +16,18 @@ func main(): = 3 sum := 0 - for x in arr: + for x in arr sum += x >> sum = 60 str := "" - for i,x in arr: + for i,x in arr str ++= "($i,$x)" >> str = "(1,10)(2,20)(3,30)" - do: + do >> arr := [10, 20] ++ [30, 40] = [10, 20, 30, 40] @@ -35,7 +35,7 @@ func main(): >> arr = [10, 20, 30, 40, 50, 60] - do: + do >> arr := [10, 20] >> copy := arr >> arr ++= [30] @@ -44,7 +44,7 @@ func main(): >> copy = [10, 20] - do: + do >> [10*i for i in 5] = [10, 20, 30, 40, 50] @@ -57,7 +57,7 @@ func main(): >> [x for x in y if x > 1 for y in [3, 4, 5] if y < 5] = [2, 3, 2, 3, 4] - do: + do >> arr := @[10, 20] >> copy := arr[] >> arr.insert(30) @@ -70,7 +70,7 @@ func main(): >> arr = @[999, 20, 30] - do: + do >> arr := &[10, 20, 30] >> reversed := arr.reversed() = [30, 20, 10] @@ -79,7 +79,7 @@ func main(): >> reversed = [30, 20, 10] - do: + do >> nums := @[10, -20, 30] # Sorted function doesn't mutate original: >> nums.sorted() @@ -91,34 +91,34 @@ func main(): >> nums = @[-20, 10, 30] # Custom sort functions: - >> nums.sort(func(x,y:&Int): x.abs() <> y.abs()) + >> nums.sort(func(x,y:&Int) x.abs() <> y.abs()) >> nums = @[10, -20, 30] - >> nums.sort(func(x,y:&Int): y[] <> x[]) + >> nums.sort(func(x,y:&Int) y[] <> x[]) >> nums = @[30, 10, -20] >> ["A", "B", "C"].sample(10, [1.0, 0.5, 0.0]) - do: + do >> heap := @[(i * 1337) mod 37 for i in 10] >> heap.heapify() >> heap heap_order : @[Int] = @[] - repeat: + repeat heap_order.insert(heap.heap_pop() or stop) >> heap_order[] == heap_order.sorted() = yes heap_order[] = [] - for i in 10: + for i in 10 heap.heap_push((i*13337) mod 37) >> heap - repeat: + repeat heap_order.insert(heap.heap_pop() or stop) >> heap_order[] == heap_order.sorted() = yes - do: + do >> [i*10 for i in 5].from(3) = [30, 40, 50] >> [i*10 for i in 5].to(3) @@ -146,17 +146,17 @@ func main(): # Test iterating over array.from() and array.to() xs := ["A", "B", "C", "D"] - for i,x in xs.to(-2): - for y in xs.from(i+1): + for i,x in xs.to(-2) + for y in xs.from(i+1) say("$(x)$(y)") - do: + do >> nums := @[-7, -4, -1, 2, 5] >> nums.sort() >> [nums.binary_search(i) for i in nums[]] = [1, 2, 3, 4, 5] - >> nums.sort(func(a,b:&Int): a.abs() <> b.abs()) - >> [nums.binary_search(i, func(a,b:&Int): a.abs() <> b.abs()) for i in nums[]] + >> nums.sort(func(a,b:&Int) a.abs() <> b.abs()) + >> [nums.binary_search(i, func(a,b:&Int) a.abs() <> b.abs()) for i in nums[]] = [1, 2, 3, 4, 5] >> ["a", "b", "c"].find("b") @@ -164,12 +164,12 @@ func main(): >> ["a", "b", "c"].find("XXX") = none - >> [10, 20].first(func(i:&Int): i.is_prime()) + >> [10, 20].first(func(i:&Int) i.is_prime()) = none - >> [4, 5, 6].first(func(i:&Int): i.is_prime()) + >> [4, 5, 6].first(func(i:&Int) i.is_prime()) = 2? - do: + do >> nums := &[10, 20, 30, 40, 50] >> nums.pop() = 50? diff --git a/test/bytes.tm b/test/bytes.tm index 862d2674..25efbeb8 100644 --- a/test/bytes.tm +++ b/test/bytes.tm @@ -1,5 +1,5 @@ -func main(): +func main() say("Test bytes:") >> Byte(100) = Byte(0x64) diff --git a/test/corecursive_func.tm b/test/corecursive_func.tm index b5f5f13e..8faf8b0d 100644 --- a/test/corecursive_func.tm +++ b/test/corecursive_func.tm @@ -1,15 +1,15 @@ -func ping(x:Int->[Text]): - if x > 0: +func ping(x:Int->[Text]) + if x > 0 return ["ping: $x"] ++ pong(x-1) - else: + else return ["ping: $x"] -func pong(x:Int->[Text]): - if x > 0: +func pong(x:Int->[Text]) + if x > 0 return ["pong: $x"] ++ ping(x-1) - else: + else return ["pong: $x"] -func main(): +func main() >> ping(3) = ["ping: 3", "pong: 2", "ping: 1", "pong: 0"] diff --git a/test/defer.tm b/test/defer.tm index 4053f0c1..e5033075 100644 --- a/test/defer.tm +++ b/test/defer.tm @@ -1,8 +1,8 @@ -func main(): +func main() x := 123 nums : @[Int] = @[] - do: - defer: + do + defer nums.insert(x) x = 999 @@ -11,28 +11,28 @@ func main(): >> x = 999 - defer: + defer say("All done!") - for word in ["first", "second", "third"]: - defer: + for word in ["first", "second", "third"] + defer say("Got $word deferred") - if word == "second": + if word == "second" say("<skipped>") skip - else if word == "third": + else if word == "third" say("<stopped>") stop - for i in 3: - defer: + for i in 3 + defer say("Inner loop deferred $i") - if i == 2: + if i == 2 say("<skipped inner>") skip - else if i == 3: + else if i == 3 say("<stopped inner>") stop @@ -40,12 +40,12 @@ func main(): say("Made it through the loop") - >> thunk := func(return_early=no): + >> thunk := func(return_early=no) say("Entering thunk") - defer: + defer say("Deferred thunk cleanup") - if return_early: + if return_early say("Returning early...") return @@ -65,20 +65,20 @@ func main(): >> counter() = 3 -func defer_func(return_early=no): +func defer_func(return_early=no) say("Entering defer_func") - defer: + defer say("Deferred defer_func cleanup") - if return_early: + if return_early say("Returning early...") return say("Finished defer_func") -func make_counter(->func(->Int)): +func make_counter(->func(->Int)) i := 1 - return func(): - defer: i += 1 + return func() + defer i += 1 return i diff --git a/test/enums.tm b/test/enums.tm index a11f95f9..3b3b31ea 100644 --- a/test/enums.tm +++ b/test/enums.tm @@ -1,21 +1,21 @@ enum Foo(Zero, One(x:Int), Two(x:Int, y:Int), Three(x:Int, y:Text, z:Bool), Four(x,y,z,w:Int), Last(t:Text)) -func choose_text(f:Foo->Text): +func choose_text(f:Foo->Text) >> f - when f is Zero: + when f is Zero return "Zero" - is One(one): + is One(one) return "One: $one" - is Two(x, y): + is Two(x, y) return "Two: x=$x, y=$y" - is Three(three): + is Three(three) return "Three: $three" - is Four: + is Four return "Four" - else: + else return "else: $f" -func main(): +func main() >> Foo.Zero = Foo.Zero >> Foo.One(123) @@ -63,24 +63,24 @@ func main(): i := 1 cases := [Foo.One(1), Foo.One(2), Foo.Zero] - while when cases[i] is One(x): + while when cases[i] is One(x) >> x i += 1 >> [ ( - when x is One(y), Two(y,_): + when x is One(y), Two(y,_) "Small $y" - is Zero: + is Zero "Zero" - else: + else "Other" ) for x in [Foo.Zero, Foo.One(1), Foo.Two(2,2), Foo.Three(3,"",no)] ] = ["Zero", "Small 1", "Small 2", "Other"] - >> expr := when cases[1] is One(y): + >> expr := when cases[1] is One(y) y + 1 - else: + else -1 = 2 diff --git a/test/extern.tm b/test/extern.tm index 17c39948..8e2b6c06 100644 --- a/test/extern.tm +++ b/test/extern.tm @@ -1,5 +1,5 @@ extern sqrt : func(n:Num->Num) -func main(): +func main() >> sqrt(4) = 2. diff --git a/test/for.tm b/test/for.tm index e4967e86..75ad2fee 100644 --- a/test/for.tm +++ b/test/for.tm @@ -1,35 +1,35 @@ -func all_nums(nums:[Int] -> Text): +func all_nums(nums:[Int] -> Text) result := "" - for num in nums: + for num in nums result ++= "$num," - else: + else return "EMPTY" return result -func labeled_nums(nums:[Int] -> Text): +func labeled_nums(nums:[Int] -> Text) result := "" - for i,num in nums: + for i,num in nums result ++= "$i:$num," - else: + else return "EMPTY" return result -func table_str(t:{Text=Text} -> Text): +func table_str(t:{Text=Text} -> Text) str := "" - for k,v in t: + for k,v in t str ++= "$k:$v," - else: return "EMPTY" + else return "EMPTY" return str -func table_key_str(t:{Text=Text} -> Text): +func table_key_str(t:{Text=Text} -> Text) str := "" - for k in t: + for k in t str ++= "$k," - else: return "EMPTY" + else return "EMPTY" return str -func main(): +func main() >> all_nums([10,20,30]) = "10,20,30," >> all_nums([]) diff --git a/test/functions.tm b/test/functions.tm index 22d950d4..17ca1e85 100644 --- a/test/functions.tm +++ b/test/functions.tm @@ -1,10 +1,10 @@ -func add(x:Int, y:Int -> Int): +func add(x:Int, y:Int -> Int) return x + y -func cached_heap(x:Int->@Int; cached): +func cached_heap(x:Int->@Int; cached) return @x -func main(): +func main() >> add(3, 5) = 8 diff --git a/test/import.tm b/test/import.tm index 960bfcbb..a6a6fa16 100644 --- a/test/import.tm +++ b/test/import.tm @@ -1,13 +1,13 @@ vectors := use ../examples/vectors/vectors.tm use ./use_import.tm -func returns_vec(->vectors.Vec2): +func returns_vec(->vectors.Vec2) return vectors.Vec2(1, 2) -func returns_imported_type(->ImportedType): +func returns_imported_type(->ImportedType) return get_value() # Imported from ./use_import.tm -func main(): +func main() >> empty : [vectors.Vec2] = [] >> returns_vec() = Vec2(x=1, y=2) diff --git a/test/inline_c.tm b/test/inline_c.tm index 3c0949cd..a79359d1 100644 --- a/test/inline_c.tm +++ b/test/inline_c.tm @@ -1,5 +1,5 @@ -func main(): +func main() >> inline C:Int32 { int x = 1 + 2; x } = Int32(3) diff --git a/test/integers.tm b/test/integers.tm index 508a38ae..72a195b4 100644 --- a/test/integers.tm +++ b/test/integers.tm @@ -1,4 +1,4 @@ -func main(): +func main() >> 2 + 3 = 5 @@ -37,7 +37,7 @@ func main(): = 1 nums := "" - for x in 5: + for x in 5 nums ++= "$x," >> nums = "1,2,3,4,5," @@ -66,7 +66,7 @@ func main(): >> Int(2.1, truncate=yes) = 2 - do: + do >> small_int := 1 = 1 >> max_small_int := 536870911 @@ -84,11 +84,11 @@ func main(): >> super_big + 1 = 10000000000000000000000 - do: + do interesting_numerators := [-999999, -100, -23, -1, 0, 1, 23, 100, 999999] interesting_denominators := [-99, -20, -17, -1, 1, 17, 20, 99] - for n in interesting_numerators: - for d in interesting_denominators: + for n in interesting_numerators + for d in interesting_denominators >> (n/d)*d + (n mod d) == n = yes diff --git a/test/iterators.tm b/test/iterators.tm index 64d21ea3..1816fd7a 100644 --- a/test/iterators.tm +++ b/test/iterators.tm @@ -1,22 +1,22 @@ struct Pair(x:Text, y:Text) -func pairwise(strs:[Text] -> func(->Pair?)): +func pairwise(strs:[Text] -> func(->Pair?)) i := 1 - return func(): - if i + 1 > strs.length: return none + return func() + if i + 1 > strs.length return none i += 1 return Pair(strs[i-1], strs[i])? -func range(first:Int, last:Int -> func(->Int?)): +func range(first:Int, last:Int -> func(->Int?)) i := first - return func(): - if i > last: + return func() + if i > last return none i += 1 return (i-1)? -func main(): +func main() values := ["A", "B", "C", "D"] >> (++: "($(foo.x)$(foo.y))" for foo in pairwise(values))! @@ -24,9 +24,9 @@ func main(): >> ["$(foo.x)$(foo.y)" for foo in pairwise(values)] = ["AB", "BC", "CD"] - do: + do result : @[Text] = @[] - for foo in pairwise(values): + for foo in pairwise(values) result.insert("$(foo.x)$(foo.y)") >> result[] = ["AB", "BC", "CD"] diff --git a/test/lambdas.tm b/test/lambdas.tm index 6e261e0f..d6501cb7 100644 --- a/test/lambdas.tm +++ b/test/lambdas.tm @@ -1,18 +1,18 @@ -func make_adder(x:Int -> func(y:Int->Int)): - return func(y:Int): x + y +func make_adder(x:Int -> func(y:Int->Int)) + return func(y:Int) x + y -func suffix_fn(fn:func(t:Text->Text), suffix:Text -> func(t:Text->Text)): - return func(t:Text): fn(t)++suffix +func suffix_fn(fn:func(t:Text->Text), suffix:Text -> func(t:Text->Text)) + return func(t:Text) fn(t)++suffix -func mul_func(n:Int, fn:func(x:Int->Int) -> func(x:Int->Int)): - return func(x:Int): n*fn(x) +func mul_func(n:Int, fn:func(x:Int->Int) -> func(x:Int->Int)) + return func(x:Int) n*fn(x) -func main(): - >> add_one := func(x:Int): x + 1 +func main() + >> add_one := func(x:Int) x + 1 >> add_one(10) = 11 - >> shout := func(msg:Text): say("$(msg.upper())!") + >> shout := func(msg:Text) say("$(msg.upper())!") >> shout("hello") >> asdf := add_one @@ -23,7 +23,7 @@ func main(): >> add_100(5) = 105 - >> shout2 := suffix_fn(func(t:Text): t.upper(), "!") + >> shout2 := suffix_fn(func(t:Text) t.upper(), "!") >> shout2("hello") = "HELLO!" @@ -33,10 +33,10 @@ func main(): # Test nested lambdas: outer := "Hello" - fn := func(): - return func(): - return func(): - defer: say("$outer") + fn := func() + return func() + return func() + defer say("$outer") return outer >> fn()()() = "Hello" diff --git a/test/lang.tm b/test/lang.tm index 6d0a94ea..081438ed 100644 --- a/test/lang.tm +++ b/test/lang.tm @@ -1,6 +1,6 @@ -lang HTML: +lang HTML HEADER := $HTML"<!DOCTYPE HTML>" - convert(t:Text->HTML): + convert(t:Text->HTML) t = t.translate({ "&"="&", "<"="<", @@ -11,17 +11,17 @@ lang HTML: return HTML.from_text(t) - convert(i:Int->HTML): + convert(i:Int->HTML) return HTML.from_text("$i") - func paragraph(content:HTML->HTML): + func paragraph(content:HTML->HTML) return $HTML"<p>$content</p>" -struct Bold(text:Text): - convert(b:Bold -> HTML): +struct Bold(text:Text) + convert(b:Bold -> HTML) return $HTML"<b>$(b.text)</b>" -func main(): +func main() >> HTML.HEADER = $HTML"<!DOCTYPE HTML>" diff --git a/test/metamethods.tm b/test/metamethods.tm index aac37c4b..9399bc9a 100644 --- a/test/metamethods.tm +++ b/test/metamethods.tm @@ -1,50 +1,50 @@ -struct Vec2(x,y:Int): - func plus(a,b:Vec2 -> Vec2; inline): +struct Vec2(x,y:Int) + 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 dot(a,b:Vec2 -> Int; inline): + func dot(a,b:Vec2 -> Int; inline) return a.x*b.x + a.y*b.y - func scaled_by(a:Vec2, k:Int -> Vec2; inline): + func scaled_by(a:Vec2, k:Int -> Vec2; inline) return Vec2(a.x*k, a.y*k) - 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 divided_by(a:Vec2, k:Int -> Vec2; inline): + func divided_by(a:Vec2, k:Int -> Vec2; inline) return Vec2(a.x/k, a.y/k) - func negative(v:Vec2 -> Vec2; inline): + func negative(v:Vec2 -> Vec2; inline) return Vec2(-v.x, -v.y) - func negated(v:Vec2 -> Vec2; inline): + func negated(v:Vec2 -> Vec2; inline) return Vec2(not v.x, not v.y) - func bit_and(a,b:Vec2 -> Vec2; inline): + func bit_and(a,b:Vec2 -> Vec2; inline) return Vec2(a.x and b.x, a.y and b.y) - func bit_or(a,b:Vec2 -> Vec2; inline): + func bit_or(a,b:Vec2 -> Vec2; inline) return Vec2(a.x or b.x, a.y or b.y) - func bit_xor(a,b:Vec2 -> Vec2; inline): + func bit_xor(a,b:Vec2 -> Vec2; inline) return Vec2(a.x xor b.x, a.y xor b.y) - func left_shifted(v:Vec2, bits:Int -> Vec2; inline): + func left_shifted(v:Vec2, bits:Int -> Vec2; inline) return Vec2(v.x >> bits, v.y >> bits) - func right_shifted(v:Vec2, bits:Int -> Vec2; inline): + func right_shifted(v:Vec2, bits:Int -> Vec2; inline) return Vec2(v.x << bits, v.y << bits) - func modulo(v:Vec2, modulus:Int -> Vec2; inline): + func modulo(v:Vec2, modulus:Int -> Vec2; inline) return Vec2(v.x mod modulus, v.y mod modulus) - func modulo1(v:Vec2, modulus:Int -> Vec2; inline): + func modulo1(v:Vec2, modulus:Int -> Vec2; inline) return Vec2(v.x mod1 modulus, v.y mod1 modulus) -func main(): +func main() >> x := Vec2(10, 20) >> y := Vec2(100, 200) >> x + y diff --git a/test/minmax.tm b/test/minmax.tm index a0f0640f..8ffb401e 100644 --- a/test/minmax.tm +++ b/test/minmax.tm @@ -1,9 +1,9 @@ -struct Foo(x:Int, y:Int): - func len(f:Foo->Num): +struct Foo(x:Int, y:Int) + func len(f:Foo->Num) return Num.sqrt(Num(f.x*f.x + f.y*f.y))! -func main(): +func main() >> 3 _min_ 5 = 3 >> 5 _min_ 3 diff --git a/test/nums.tm b/test/nums.tm index e5de1e22..573b50ec 100644 --- a/test/nums.tm +++ b/test/nums.tm @@ -1,4 +1,4 @@ -func main(): +func main() >> n := 1.5 = 1.5 diff --git a/test/optionals.tm b/test/optionals.tm index bf3e1633..d81d065f 100644 --- a/test/optionals.tm +++ b/test/optionals.tm @@ -1,74 +1,74 @@ -struct Struct(x:Int, y:Text): - func maybe(should_i:Bool->Struct?): - if should_i: +struct Struct(x:Int, y:Text) + func maybe(should_i:Bool->Struct?) + if should_i return Struct(123, "hello") - else: + else return none -enum Enum(X, Y(y:Int)): - func maybe(should_i:Bool->Enum?): - if should_i: +enum Enum(X, Y(y:Int)) + func maybe(should_i:Bool->Enum?) + if should_i return Enum.Y(123) - else: + else return none -func maybe_int(should_i:Bool->Int?): - if should_i: +func maybe_int(should_i:Bool->Int?) + if should_i return 123 - else: + else return none -func maybe_int64(should_i:Bool->Int64?): - if should_i: +func maybe_int64(should_i:Bool->Int64?) + if should_i return Int64(123) - else: + else return none -func maybe_array(should_i:Bool->[Int]?): - if should_i: +func maybe_array(should_i:Bool->[Int]?) + if should_i return [10, 20, 30] - else: + else return none -func maybe_bool(should_i:Bool->Bool?): - if should_i: +func maybe_bool(should_i:Bool->Bool?) + if should_i return no - else: + else return none -func maybe_text(should_i:Bool->Text?): - if should_i: +func maybe_text(should_i:Bool->Text?) + if should_i return "Hello" - else: + else return none -func maybe_num(should_i:Bool->Num?): - if should_i: +func maybe_num(should_i:Bool->Num?) + if should_i return 12.3 - else: + else return none -func maybe_lambda(should_i:Bool-> func()?): - if should_i: - return func(): say("hi!") - else: +func maybe_lambda(should_i:Bool-> func()?) + if should_i + return func() say("hi!") + else return none -func maybe_c_string(should_i:Bool->CString?): - if should_i: +func maybe_c_string(should_i:Bool->CString?) + if should_i return ("hi".as_c_string())? - else: + else return none -func main(): +func main() >> 5? = 5? - >> if no: + >> if no x : Int? = none x - else: + else 5 = 5? @@ -85,157 +85,157 @@ func main(): >> none_int or -1 = -1 - do: + do say("Ints:") >> yep := maybe_int(yes) = 123? >> nope := maybe_int(no) = none - >> if yep: + >> if yep >> yep = 123 - else: fail("Falsey: $yep") - >> if nope: + else fail("Falsey: $yep") + >> if nope fail("Truthy: $nope") - else: say("Falsey: $nope") + else say("Falsey: $nope") - do: + do say("Int64s:") >> yep := maybe_int64(yes) = Int64(123)? >> nope := maybe_int64(no) = none - >> if yep: + >> if yep >> yep = Int64(123) - else: fail("Falsey: $yep") - >> if nope: + else fail("Falsey: $yep") + >> if nope fail("Truthy: $nope") - else: say("Falsey: $nope") + else say("Falsey: $nope") - do: + do say("Arrays:") >> yep := maybe_array(yes) = [10, 20, 30]? >> nope := maybe_array(no) = none - >> if yep: + >> if yep >> yep = [10, 20, 30] - else: fail("Falsey: $yep") - >> if nope: + else fail("Falsey: $yep") + >> if nope fail("Truthy: $nope") - else: say("Falsey: $nope") + else say("Falsey: $nope") - do: + do say("...") say("Bools:") >> yep := maybe_bool(yes) = no? >> nope := maybe_bool(no) = none - >> if yep: + >> if yep >> yep = no - else: fail("Falsey: $yep") - >> if nope: + else fail("Falsey: $yep") + >> if nope fail("Truthy: $nope") - else: say("Falsey: $nope") + else say("Falsey: $nope") - do: + do say("...") say("Text:") >> yep := maybe_text(yes) = "Hello"? >> nope := maybe_text(no) = none - >> if yep: + >> if yep >> yep = "Hello" - else: fail("Falsey: $yep") - >> if nope: + else fail("Falsey: $yep") + >> if nope fail("Truthy: $nope") - else: say("Falsey: $nope") + else say("Falsey: $nope") - do: + do say("...") say("Nums:") >> yep := maybe_num(yes) = 12.3? >> nope := maybe_num(no) = none - >> if yep: + >> if yep >> yep = 12.3 - else: fail("Falsey: $yep") - >> if nope: + else fail("Falsey: $yep") + >> if nope fail("Truthy: $nope") - else: say("Falsey: $nope") + else say("Falsey: $nope") - do: + do say("...") say("Lambdas:") # >> yep := maybe_lambda(yes) # = func() [optionals.tm:54] : func()? >> nope := maybe_lambda(no) = none - # >> if yep: + # >> if yep # >> yep # = func() [optionals.tm:54] - # else: fail("Falsey: $yep") - >> if nope: + # else fail("Falsey: $yep") + >> if nope fail("Truthy: $nope") - else: say("Falsey: $nope") + else say("Falsey: $nope") - do: + do say("...") say("Structs:") >> yep := Struct.maybe(yes) = Struct(x=123, y="hello")? >> nope := Struct.maybe(no) = none - >> if yep: + >> if yep >> yep = Struct(x=123, y="hello") - else: fail("Falsey: $yep") - >> if nope: + else fail("Falsey: $yep") + >> if nope fail("Truthy: $nope") - else: say("Falsey: $nope") + else say("Falsey: $nope") - do: + do say("...") say("Enums:") >> yep := Enum.maybe(yes) = Enum.Y(123)? >> nope := Enum.maybe(no) = none - >> if yep: + >> if yep >> yep = Enum.Y(123) - else: fail("Falsey: $yep") - >> if nope: + else fail("Falsey: $yep") + >> if nope fail("Truthy: $nope") - else: say("Falsey: $nope") + else say("Falsey: $nope") - do: + do say("...") say("C Strings:") >> yep := maybe_c_string(yes) = CString("hi")? >> nope := maybe_c_string(no) = none - >> if yep: + >> if yep >> yep = CString("hi") - else: fail("Falsey: $yep") - >> if nope: + else fail("Falsey: $yep") + >> if nope fail("Truthy: $nope") - else: say("Falsey: $nope") + else say("Falsey: $nope") - if yep := maybe_int(yes): + if yep := maybe_int(yes) >> yep = 123 - else: fail("Unreachable") + else fail("Unreachable") >> maybe_int(yes)! = 123 @@ -251,32 +251,32 @@ func main(): >> [5?, none, none, 6?].sorted() = [none, none, 5, 6] - do: - >> value := if var := 5?: + do + >> value := if var := 5? var - else: + else 0 = 5 - do: + do >> value := if var : Int? = none: var - else: + else 0 = 0 - do: + do >> opt := 5? - >> if opt: + >> if opt >> opt - else: + else >> opt - do: + do >> opt : Int? = none - >> if opt: + >> if opt >> opt - else: + else >> opt >> not 5? @@ -289,7 +289,7 @@ func main(): >> [Struct(5,"A")?, Struct(6,"B"), Struct(7,"C")] = [Struct(x=5, y="A")?, Struct(x=6, y="B")?, Struct(x=7, y="C")?] - if 5? or no: + if 5? or no say("Binary op 'or' works with optionals") - else: + else fail("Failed to do binary op 'or' on optional") diff --git a/test/paths.tm b/test/paths.tm index 85f9b191..700a0393 100644 --- a/test/paths.tm +++ b/test/paths.tm @@ -1,5 +1,5 @@ # Tests for file paths -func main(): +func main() >> (/).exists() = yes >> (~/).exists() @@ -29,19 +29,19 @@ func main(): >> tmpdir.files().has(tmpfile) = yes - if tmp_lines := tmpfile.by_line(): + if tmp_lines := tmpfile.by_line() >> [line for line in tmp_lines] = ["Hello world!"] - else: + else fail("Couldn't read lines in $tmpfile") >> (./does-not-exist.xxx).read() = none >> (./does-not-exist.xxx).read_bytes() = none - if lines := (./does-not-exist.xxx).by_line(): + if lines := (./does-not-exist.xxx).by_line() fail("I could read lines in a nonexistent file") - else: + else pass >> tmpfile.remove() diff --git a/test/reductions.tm b/test/reductions.tm index c56c7d09..2666e1a9 100644 --- a/test/reductions.tm +++ b/test/reductions.tm @@ -1,6 +1,6 @@ struct Foo(x,y:Int) -func main(): +func main() >> (+: [10, 20, 30]) = 60? diff --git a/test/serialization.tm b/test/serialization.tm index b3ccc67a..457442e5 100644 --- a/test/serialization.tm +++ b/test/serialization.tm @@ -3,45 +3,45 @@ struct Foo(name:Text, next:@Foo?=none) enum MyEnum(Zero, One(x:Int), Two(x:Num, y:Text)) -func main(): - do: +func main() + do >> obj := Int64(123) >> bytes := obj.serialized() >> deserialize(bytes -> Int64) == obj = yes - do: + do >> obj := 5 >> bytes := obj.serialized() >> deserialize(bytes -> Int) == obj = yes - do: + do >> obj := 9999999999999999999999999999999999999999999999999999 >> bytes := obj.serialized() >> deserialize(bytes -> Int) == obj = yes - do: + do >> obj := "Héllo" >> bytes := obj.serialized() >> deserialize(bytes -> Text) >> deserialize(bytes -> Text) == obj = yes - do: + do >> obj := [Int64(10), Int64(20), Int64(30)].reversed() >> bytes := obj.serialized() >> deserialize(bytes -> [Int64]) == obj = yes - do: + do >> obj := yes >> bytes := obj.serialized() >> deserialize(bytes -> Bool) == obj = yes - do: + do >> obj := @[10, 20] >> bytes := obj.serialized() >> roundtrip := deserialize(bytes -> @[Int]) @@ -50,46 +50,46 @@ func main(): >> roundtrip[] == obj[] = yes - do: + do >> obj := {"A"=10, "B"=20; fallback={"C"=30}} >> bytes := obj.serialized() >> deserialize(bytes -> {Text=Int}) == obj = yes - do: + do >> obj := @Foo("root") >> obj.next = @Foo("abcdef", next=obj) >> bytes := obj.serialized() >> deserialize(bytes -> @Foo) # = @Foo(name="root", next=@Foo(name="abcdef", next=@~1)) - do: + do >> obj := MyEnum.Two(123, "OKAY") >> bytes := obj.serialized() >> deserialize(bytes -> MyEnum) == obj = yes - do: + do >> obj := "Hello"? >> bytes := obj.serialized() >> deserialize(bytes -> Text?) == obj = yes - do: + do >> obj := {10, 20, 30} >> bytes := obj.serialized() >> deserialize(bytes -> {Int}) == obj = yes - do: + do >> obj : Num? = none >> bytes := obj.serialized() >> deserialize(bytes -> Num?) == obj = yes - do: + do cases := [0, -1, 1, 10, 100000, 999999999999999999999999999] - for i in cases: + for i in cases >> bytes := i.serialized() >> deserialize(bytes -> Int) == i = yes diff --git a/test/sets.tm b/test/sets.tm index 5179947e..1c395fba 100644 --- a/test/sets.tm +++ b/test/sets.tm @@ -1,5 +1,5 @@ -func main(): +func main() >> t1 := @{10, 20, 30, 10} = @{10, 20, 30} >> t1.has(10) diff --git a/test/structs.tm b/test/structs.tm index cf7b6a1c..f1ae49d0 100644 --- a/test/structs.tm +++ b/test/structs.tm @@ -8,7 +8,7 @@ struct Password(text:Text; secret) struct CorecursiveA(other:@CorecursiveB?) struct CorecursiveB(other:@CorecursiveA?=none) -func test_literals(): +func test_literals() >> Single(123) = Single(123) >> x := Pair(10, 20) @@ -20,7 +20,7 @@ func test_literals(): >> x == Pair(-1, -2) = no -func test_metamethods(): +func test_metamethods() >> x := Pair(10, 20) >> y := Pair(100, 200) >> x == y @@ -38,7 +38,7 @@ func test_metamethods(): >> set.has(y) = no -func test_mixed(): +func test_mixed() >> x := Mixed(10, "Hello") >> y := Mixed(99, "Hello") >> x == y @@ -55,14 +55,14 @@ func test_mixed(): >> set.has(y) = no -func test_text(): +func test_text() >> b := @CorecursiveB() >> a := @CorecursiveA(b) >> b.other = a >> a # = @CorecursiveA(@CorecursiveB(@~1)) -func main(): +func main() test_literals() test_metamethods() test_mixed() diff --git a/test/tables.tm b/test/tables.tm index a5f51520..67299142 100644 --- a/test/tables.tm +++ b/test/tables.tm @@ -1,4 +1,4 @@ -func main(): +func main() >> t := {"one"=1, "two"=2} = {"one"=1, "two"=2} @@ -14,7 +14,7 @@ func main(): = -1 t_str := "" - for k,v in t: + for k,v in t t_str ++= "($k=$v)" >> t_str = "(one=1)(two=2)" @@ -45,7 +45,7 @@ func main(): = {"one"=1, "two"=2}? t2_str := "" - for k,v in t2: + for k,v in t2 t2_str ++= "($k=$v)" >> t2_str = "(three=3)" @@ -62,7 +62,7 @@ func main(): >> t3 = @{1=10, 2=20} - do: + do >> plain := {1=10, 2=20, 3=30} >> plain[2]! = 20 @@ -81,14 +81,14 @@ func main(): >> fallback[1] or -999 = 10 - do: + do >> t4 := &{"one"= 1} >> t4["one"] = 999 >> t4["two"] = 222 >> t4 = &{"one"=999, "two"=222} - do: + do >> {1=1, 2=2} == {2=2, 1=1} = yes >> {1=1, 2=2} == {1=1, 2=999} @@ -102,7 +102,7 @@ func main(): >> other_ints : [{Int}] = [{/}, {1}, {2}, {99}, {0, 3}, {1, 2}, {99}].sorted() = [{/}, {0, 3}, {1}, {1, 2}, {2}, {99}, {99}] - do: + do # Default values: counter := &{"x"=10; default=0} >> counter["x"] diff --git a/test/text.tm b/test/text.tm index b353aa26..1cabbdea 100644 --- a/test/text.tm +++ b/test/text.tm @@ -1,4 +1,4 @@ -func main(): +func main() str := "Hello Amélie!" say("Testing strings like $str") @@ -236,7 +236,7 @@ func main(): >> ("hello" ++ " " ++ "Amélie").reversed() = "eilémA olleh" - do: + do say("Testing concatenation-stability:") ab := Text.from_codepoint_names(["LATIN SMALL LETTER E", "COMBINING VERTICAL LINE BELOW"])! >> ab.codepoint_names() @@ -254,7 +254,7 @@ func main(): = 1 - do: + do concat := "e" ++ Text.from_codepoints([Int32(0x300)]) >> concat.length = 1 diff --git a/test/use_import.tm b/test/use_import.tm index 714c26b0..a56d9cbb 100644 --- a/test/use_import.tm +++ b/test/use_import.tm @@ -2,8 +2,8 @@ struct ImportedType(name:Text) needs_initializing := 999999999999999999 -func get_value(->ImportedType): +func get_value(->ImportedType) return ImportedType("Hello") -func main(): +func main() pass diff --git a/test/when.tm b/test/when.tm index d93745dd..28573d88 100644 --- a/test/when.tm +++ b/test/when.tm @@ -1,13 +1,13 @@ # Tests for the 'when' block -func main(): +func main() answers := [ ( - when x is "A","B": + when x is "A","B" "A or B" - is "C": + is "C" "C" - else: + else "Other" ) for x in ["A", "B", "C", "D"] ] @@ -15,7 +15,7 @@ func main(): = ["A or B", "A or B", "C", "Other"] n := 23 - >> when n is 1: Int64(1) - is 2: Int64(2) - is 21 + 2: Int64(23) + >> when n is 1 Int64(1) + is 2 Int64(2) + is 21 + 2 Int64(23) = Int64(23)? |
