aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorBruce Hill <bruce@bruce-hill.com>2025-09-01 16:44:58 -0400
committerBruce Hill <bruce@bruce-hill.com>2025-09-01 16:44:58 -0400
commited50c5fefb8892ad2ba5262491669f268ddbd436 (patch)
treee93a6a8b7e963c37a001691751d6845d10e0cbf8 /examples
parent02a99d24a310c04622a875dcf4b0c6fd2de71332 (diff)
Overhaul code to stop keeping examples and libraries in the same repo,
but instead spin each out into its own repo.
Diffstat (limited to 'examples')
-rw-r--r--examples/README.md19
-rw-r--r--examples/colorful/CHANGES.md13
-rw-r--r--examples/colorful/README.md70
-rw-r--r--examples/colorful/colorful.tm220
-rw-r--r--examples/colorful/modules.ini2
-rw-r--r--examples/colorful/test.colors9
-rw-r--r--examples/coroutines/ACO_LICENSE202
-rw-r--r--examples/coroutines/CHANGES.md5
-rw-r--r--examples/coroutines/README.md30
-rw-r--r--examples/coroutines/aco.c458
-rw-r--r--examples/coroutines/aco.h213
-rw-r--r--examples/coroutines/acoyield.S208
-rw-r--r--examples/coroutines/coroutines.tm67
-rw-r--r--examples/game/CHANGES.md5
-rw-r--r--examples/game/Makefile17
-rw-r--r--examples/game/README.md13
-rw-r--r--examples/game/box.tm7
-rw-r--r--examples/game/game.tm29
-rw-r--r--examples/game/map.txt18
-rw-r--r--examples/game/player.tm28
-rw-r--r--examples/game/raylib.tm63
-rw-r--r--examples/game/world.tm88
-rw-r--r--examples/hello.tm3
-rw-r--r--examples/http-server/CHANGES.md5
-rw-r--r--examples/http-server/README.md8
-rw-r--r--examples/http-server/connection-queue.tm25
-rw-r--r--examples/http-server/http-server.tm149
-rw-r--r--examples/http-server/modules.ini8
-rw-r--r--examples/http-server/sample-site/foo.html6
-rw-r--r--examples/http-server/sample-site/hello.txt1
-rw-r--r--examples/http-server/sample-site/index.html16
-rwxr-xr-xexamples/http-server/sample-site/random.tm17
-rw-r--r--examples/http-server/sample-site/styles.css11
-rw-r--r--examples/http/CHANGES.md5
-rw-r--r--examples/http/http.tm111
-rw-r--r--examples/ini/CHANGES.md5
-rw-r--r--examples/ini/ini.tm61
-rw-r--r--examples/ini/modules.ini2
-rw-r--r--examples/ini/test.ini8
-rw-r--r--examples/log/CHANGES.md5
-rw-r--r--examples/log/log.tm50
-rw-r--r--examples/vectors/CHANGES.md5
-rw-r--r--examples/vectors/vectors.tm136
-rw-r--r--examples/wrap/CHANGES.md5
-rw-r--r--examples/wrap/wrap.tm102
45 files changed, 13 insertions, 2515 deletions
diff --git a/examples/README.md b/examples/README.md
index 9e9291e5..d527d692 100644
--- a/examples/README.md
+++ b/examples/README.md
@@ -4,19 +4,20 @@ This folder contains some example programs and libraries.
## Example Programs
+- [hello.tm](hello.tm): A Hello World program.
- [learnxiny.tm](learnxiny.tm): A quick overview of language features in the
style of [learnxinyminutes.com](https://learnxinyminutes.com/).
-- [game](game/): An example game using raylib.
-- [http-server](http-server/): A multithreaded HTTP server.
-- [wrap](wrap/): A command-line program to wrap text.
+- [game](https://github.com/bruce-hill/tomo-game): An example game using raylib.
+- [http-server](https://github.com/bruce-hill/http-server): A multithreaded HTTP server.
+- [wrap](https://github.com/bruce-hill/wrap): A command-line program to wrap text.
## Example Libraries
Libraries can be installed with `tomo -IL ./library-folder`
-- [colorful](colorful/): A DSL useful for rendering terminal colors.
-- [coroutines](coroutines/): A library for stackful coroutines similar to Lua's. (Note: only works on x86_64)
-- [http](http/): An HTTP library to make basic synchronous HTTP requests.
-- [ini](ini/): An INI configuration file reader tool.
-- [log](log/): A logging utility.
-- [vectors](vectors/): A math vector library.
+- [colorful](https://github.com/bruce-hill/tomo-colorful): A DSL useful for rendering terminal colors.
+- [coroutines](https://github.com/bruce-hill/tomo-coroutines): A library for stackful coroutines similar to Lua's. (Note: only works on x86_64)
+- [http](https://github.com/bruce-hill/tomo-http): An HTTP library to make basic synchronous HTTP requests.
+- [ini](https://github.com/bruce-hill/tomo-ini): An INI configuration file reader tool.
+- [log](https://github.com/bruce-hill/tomo-log): A logging utility.
+- [vectors](https://github.com/bruce-hill/tomo-vectors): A math vector library.
diff --git a/examples/colorful/CHANGES.md b/examples/colorful/CHANGES.md
deleted file mode 100644
index 093cc077..00000000
--- a/examples/colorful/CHANGES.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Version History
-
-## v1.2
-
-- Version bump for patterns.
-
-## v1.1
-
-- Added syntax for `@(strikethrough:...)` or `@(s:...)`
-
-## v1.0
-
-Initial version
diff --git a/examples/colorful/README.md b/examples/colorful/README.md
deleted file mode 100644
index faded9b1..00000000
--- a/examples/colorful/README.md
+++ /dev/null
@@ -1,70 +0,0 @@
-# Colorful Lang
-
-Colorful is a `lang` that lets you write colorful text for the terminal without
-having to stress about managing state for color highlighting.
-
-## Grammar
-
-The grammar looks like this:
-
-```
-colorful <- ("@(at)" / "@(lparen)" / "@(rparen)" # Escapes
- / "@(" attributes ":" colorful ")" # Colorful text
- / .)* # Plain text
-
-attributes <- (attribute ("," attribute)*)?
-
-attribute <- color # Color defaults to foreground
- / "fg=" color # Foreground color
- / "bg=" color # Background color
- / "ul=" color # Underline color
- / "b" / "bold"
- / "d" / "dim"
- / "u" / "underline"
- / "i" / "italic"
- / "B" / "blink"
- / "r" / "reverse"
- # These are rarely supported by terminals:
- / "fraktur"
- / "frame"
- / "encircle"
- / "overline"
- / "super" / "superscript"
- / "sub" / "subscript"
-
-color <- "black" / "red" / "green" / "yellow" / "blue" / "magenta" / "cyan" / "white"
- # All caps colors are "bright" colors (not always supported):
- / "BLACK" / "RED" / "GREEN" / "YELLOW" / "BLUE" / "MAGENTA" / "CYAN" / "WHITE"
- / "default"
- / "#" 6 hex # Values 0x000000-0xFFFFFF
- / "#" 3 hex # Values 0x000-0xFFF
- / 1-3 digit # Values 0-255
-```
-
-## Command Line Usage
-
-You can run `colorful` as a standalone executable to render colorful text with
-ANSI escape sequences so it looks nice on a terminal.
-
-```
-colorful [--help] [texts...] [--by-line] [--files ...]
-```
-
-## Library Usage
-
-`colorful` can also be used as a Tomo library:
-
-```ini
-# modules.ini
-[colorful]
-version=v1.0
-```
-
-```tomo
-use colorful
-
-$Colorful"
- @(blue:Welcome to the @(bold:party)!)
- We have @(green,bold:colors)!
-".print()
-```
diff --git a/examples/colorful/colorful.tm b/examples/colorful/colorful.tm
deleted file mode 100644
index 5b01cfd5..00000000
--- a/examples/colorful/colorful.tm
+++ /dev/null
@@ -1,220 +0,0 @@
-# Colorful language
-
-HELP := "
- colorful: A domain-specific language for writing colored text to the terminal
- Usage: colorful [args...] [--by-line] [--files files...]
-"
-
-CSI := "\033["
-
-use patterns
-
-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")
-
- func for_terminal(c:Colorful -> Text)
- return CSI ++ "m" ++ _for_terminal(c, _TermState())
-
- 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
- colorful := Colorful.from_text(text)
- colorful.print(newline=no)
- if i == texts.length then say("")
- else say(" ", newline=no)
-
- 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")
- colorful := Colorful.from_text(line)
- colorful.print()
- 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))
-
-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
- 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}/)
- 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)
- 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
- 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
- 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
- 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
- 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
- return "58;2;$((hex >> 16) and 0xFF);$((hex >> 8) and 0xFF);$((hex >> 0) and 0xFF)"
- is Default
- return "59"
- 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
- sequences.insert(apply)
- else if cur and not new
- sequences.insert(unapply)
-
-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
- sequences.insert(unapply)
- cur1, cur2 = no, no # Wiped out
-
- if new1 and not cur1
- sequences.insert(apply1)
- if new2 and not cur2
- sequences.insert(apply2)
-
-struct _TermState(
- bold=no, dim=no, italic=no, underline=no, blink=no,
- 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)
- 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")
- _toggle(sequences, old.underline, new.underline, "4", "24")
- _toggle(sequences, old.blink, new.blink, "5", "25")
- _toggle(sequences, old.reverse, new.reverse, "7", "27")
- _toggle(sequences, old.conceal, new.conceal, "8", "28")
- _toggle(sequences, old.strikethrough, new.strikethrough, "9", "29")
- _toggle2(sequences, old.frame, old.encircle, new.frame, new.frame, "51", "52", "54")
- _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
- sequences.insert(new.bg.bg())
-
- if new.fg != old.fg
- sequences.insert(new.fg.fg())
-
- if new.underline_color != old.underline_color
- sequences.insert(new.underline_color.underline())
-
- 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 "@"
- 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=")
- new_state.fg = _Color.from_text(attr.from(4))!
- else if attr.starts_with("bg=")
- new_state.bg = _Color.from_text(attr.from(4))!
- else if attr.starts_with("ul=")
- new_state.underline_color = _Color.from_text(attr.from(4))!
- else if color := _Color.from_text(attr)
- new_state.fg = color
- else if attr == "b" or attr == "bold"
- new_state.bold = yes
- else if attr == "d" or attr == "dim"
- new_state.dim = yes
- else if attr == "i" or attr == "italic"
- new_state.italic = yes
- else if attr == "u" or attr == "underline"
- new_state.underline = yes
- else if attr == "s" or attr == "strikethrough"
- new_state.strikethrough = yes
- else if attr == "B" or attr == "blink"
- new_state.blink = yes
- else if attr == "r" or attr == "reverse"
- new_state.reverse = yes
- else if attr == "fraktur"
- new_state.fraktur = yes
- else if attr == "frame"
- new_state.frame = yes
- else if attr == "encircle"
- new_state.encircle = yes
- else if attr == "overline"
- new_state.overline = yes
- else if attr == "super" or attr == "superscript"
- new_state.superscript = yes
- else if attr == "sub" or attr == "subscript"
- new_state.subscript = yes
- 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 ++= new_state.apply(prev_state)
- return result
diff --git a/examples/colorful/modules.ini b/examples/colorful/modules.ini
deleted file mode 100644
index 5e4b5b0a..00000000
--- a/examples/colorful/modules.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[patterns]
-version=v1.1
diff --git a/examples/colorful/test.colors b/examples/colorful/test.colors
deleted file mode 100644
index 314b8b05..00000000
--- a/examples/colorful/test.colors
+++ /dev/null
@@ -1,9 +0,0 @@
-This is some text that has @(bold:@(red:c)@(yellow:o)@(green:l)@(cyan:o)@(blue:r)@(magenta:s))!
-
-@(fg=#aaf,b:You can have @(red:nested) color directives and stuff (even in
-parens) will be handled @(i:right))
-
-@(dim:The output @(bold:ANSI) sequences will be optimal, even if you have
-nested stuff like bold and dim)
-
-(which is distinct from @(bold:BOLD) by itself)
diff --git a/examples/coroutines/ACO_LICENSE b/examples/coroutines/ACO_LICENSE
deleted file mode 100644
index ef4f82f0..00000000
--- a/examples/coroutines/ACO_LICENSE
+++ /dev/null
@@ -1,202 +0,0 @@
- Apache License
- Version 2.0, January 2004
- http://www.apache.org/licenses/
-
- TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
-
- 1. Definitions.
-
- "License" shall mean the terms and conditions for use, reproduction,
- and distribution as defined by Sections 1 through 9 of this document.
-
- "Licensor" shall mean the copyright owner or entity authorized by
- the copyright owner that is granting the License.
-
- "Legal Entity" shall mean the union of the acting entity and all
- other entities that control, are controlled by, or are under common
- control with that entity. For the purposes of this definition,
- "control" means (i) the power, direct or indirect, to cause the
- direction or management of such entity, whether by contract or
- otherwise, or (ii) ownership of fifty percent (50%) or more of the
- outstanding shares, or (iii) beneficial ownership of such entity.
-
- "You" (or "Your") shall mean an individual or Legal Entity
- exercising permissions granted by this License.
-
- "Source" form shall mean the preferred form for making modifications,
- including but not limited to software source code, documentation
- source, and configuration files.
-
- "Object" form shall mean any form resulting from mechanical
- transformation or translation of a Source form, including but
- not limited to compiled object code, generated documentation,
- and conversions to other media types.
-
- "Work" shall mean the work of authorship, whether in Source or
- Object form, made available under the License, as indicated by a
- copyright notice that is included in or attached to the work
- (an example is provided in the Appendix below).
-
- "Derivative Works" shall mean any work, whether in Source or Object
- form, that is based on (or derived from) the Work and for which the
- editorial revisions, annotations, elaborations, or other modifications
- represent, as a whole, an original work of authorship. For the purposes
- of this License, Derivative Works shall not include works that remain
- separable from, or merely link (or bind by name) to the interfaces of,
- the Work and Derivative Works thereof.
-
- "Contribution" shall mean any work of authorship, including
- the original version of the Work and any modifications or additions
- to that Work or Derivative Works thereof, that is intentionally
- submitted to Licensor for inclusion in the Work by the copyright owner
- or by an individual or Legal Entity authorized to submit on behalf of
- the copyright owner. For the purposes of this definition, "submitted"
- means any form of electronic, verbal, or written communication sent
- to the Licensor or its representatives, including but not limited to
- communication on electronic mailing lists, source code control systems,
- and issue tracking systems that are managed by, or on behalf of, the
- Licensor for the purpose of discussing and improving the Work, but
- excluding communication that is conspicuously marked or otherwise
- designated in writing by the copyright owner as "Not a Contribution."
-
- "Contributor" shall mean Licensor and any individual or Legal Entity
- on behalf of whom a Contribution has been received by Licensor and
- subsequently incorporated within the Work.
-
- 2. Grant of Copyright License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- copyright license to reproduce, prepare Derivative Works of,
- publicly display, publicly perform, sublicense, and distribute the
- Work and such Derivative Works in Source or Object form.
-
- 3. Grant of Patent License. Subject to the terms and conditions of
- this License, each Contributor hereby grants to You a perpetual,
- worldwide, non-exclusive, no-charge, royalty-free, irrevocable
- (except as stated in this section) patent license to make, have made,
- use, offer to sell, sell, import, and otherwise transfer the Work,
- where such license applies only to those patent claims licensable
- by such Contributor that are necessarily infringed by their
- Contribution(s) alone or by combination of their Contribution(s)
- with the Work to which such Contribution(s) was submitted. If You
- institute patent litigation against any entity (including a
- cross-claim or counterclaim in a lawsuit) alleging that the Work
- or a Contribution incorporated within the Work constitutes direct
- or contributory patent infringement, then any patent licenses
- granted to You under this License for that Work shall terminate
- as of the date such litigation is filed.
-
- 4. Redistribution. You may reproduce and distribute copies of the
- Work or Derivative Works thereof in any medium, with or without
- modifications, and in Source or Object form, provided that You
- meet the following conditions:
-
- (a) You must give any other recipients of the Work or
- Derivative Works a copy of this License; and
-
- (b) You must cause any modified files to carry prominent notices
- stating that You changed the files; and
-
- (c) You must retain, in the Source form of any Derivative Works
- that You distribute, all copyright, patent, trademark, and
- attribution notices from the Source form of the Work,
- excluding those notices that do not pertain to any part of
- the Derivative Works; and
-
- (d) If the Work includes a "NOTICE" text file as part of its
- distribution, then any Derivative Works that You distribute must
- include a readable copy of the attribution notices contained
- within such NOTICE file, excluding those notices that do not
- pertain to any part of the Derivative Works, in at least one
- of the following places: within a NOTICE text file distributed
- as part of the Derivative Works; within the Source form or
- documentation, if provided along with the Derivative Works; or,
- within a display generated by the Derivative Works, if and
- wherever such third-party notices normally appear. The contents
- of the NOTICE file are for informational purposes only and
- do not modify the License. You may add Your own attribution
- notices within Derivative Works that You distribute, alongside
- or as an addendum to the NOTICE text from the Work, provided
- that such additional attribution notices cannot be construed
- as modifying the License.
-
- You may add Your own copyright statement to Your modifications and
- may provide additional or different license terms and conditions
- for use, reproduction, or distribution of Your modifications, or
- for any such Derivative Works as a whole, provided Your use,
- reproduction, and distribution of the Work otherwise complies with
- the conditions stated in this License.
-
- 5. Submission of Contributions. Unless You explicitly state otherwise,
- any Contribution intentionally submitted for inclusion in the Work
- by You to the Licensor shall be under the terms and conditions of
- this License, without any additional terms or conditions.
- Notwithstanding the above, nothing herein shall supersede or modify
- the terms of any separate license agreement you may have executed
- with Licensor regarding such Contributions.
-
- 6. Trademarks. This License does not grant permission to use the trade
- names, trademarks, service marks, or product names of the Licensor,
- except as required for reasonable and customary use in describing the
- origin of the Work and reproducing the content of the NOTICE file.
-
- 7. Disclaimer of Warranty. Unless required by applicable law or
- agreed to in writing, Licensor provides the Work (and each
- Contributor provides its Contributions) on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
- implied, including, without limitation, any warranties or conditions
- of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
- PARTICULAR PURPOSE. You are solely responsible for determining the
- appropriateness of using or redistributing the Work and assume any
- risks associated with Your exercise of permissions under this License.
-
- 8. Limitation of Liability. In no event and under no legal theory,
- whether in tort (including negligence), contract, or otherwise,
- unless required by applicable law (such as deliberate and grossly
- negligent acts) or agreed to in writing, shall any Contributor be
- liable to You for damages, including any direct, indirect, special,
- incidental, or consequential damages of any character arising as a
- result of this License or out of the use or inability to use the
- Work (including but not limited to damages for loss of goodwill,
- work stoppage, computer failure or malfunction, or any and all
- other commercial damages or losses), even if such Contributor
- has been advised of the possibility of such damages.
-
- 9. Accepting Warranty or Additional Liability. While redistributing
- the Work or Derivative Works thereof, You may choose to offer,
- and charge a fee for, acceptance of support, warranty, indemnity,
- or other liability obligations and/or rights consistent with this
- License. However, in accepting such obligations, You may act only
- on Your own behalf and on Your sole responsibility, not on behalf
- of any other Contributor, and only if You agree to indemnify,
- defend, and hold each Contributor harmless for any liability
- incurred by, or claims asserted against, such Contributor by reason
- of your accepting any such warranty or additional liability.
-
- END OF TERMS AND CONDITIONS
-
- APPENDIX: How to apply the Apache License to your work.
-
- To apply the Apache License to your work, attach the following
- boilerplate notice, with the fields enclosed by brackets "[]"
- replaced with your own identifying information. (Don't include
- the brackets!) The text should be enclosed in the appropriate
- comment syntax for the file format. We also recommend that a
- file or class name and description of purpose be included on the
- same "printed page" as the copyright notice for easier
- identification within third-party archives.
-
- Copyright [2018] [Sen Han <00hnes@gmail.com>]
- Copyright [2024] [Bruce Hill <bruce@bruce-hill.com>]
-
- Licensed under the Apache License, Version 2.0 (the "License");
- you may not use this file except in compliance with the License.
- You may obtain a copy of the License at
-
- http://www.apache.org/licenses/LICENSE-2.0
-
- Unless required by applicable law or agreed to in writing, software
- distributed under the License is distributed on an "AS IS" BASIS,
- WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- See the License for the specific language governing permissions and
- limitations under the License.
diff --git a/examples/coroutines/CHANGES.md b/examples/coroutines/CHANGES.md
deleted file mode 100644
index 42ae752c..00000000
--- a/examples/coroutines/CHANGES.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Version History
-
-## v1.0
-
-Initial version
diff --git a/examples/coroutines/README.md b/examples/coroutines/README.md
deleted file mode 100644
index 644c0e07..00000000
--- a/examples/coroutines/README.md
+++ /dev/null
@@ -1,30 +0,0 @@
-# Tomo Coroutine Library
-
-This is a coroutine library built on top of a modified version of
-[libaco](https://libaco.org).
-
-## Example Usage
-
-```ini
-# modules.ini
-[coroutines]
-version=v1.0
-```
-
-```tomo
-use coroutines
-
-func main()
- co := Coroutine(func()
- say("I'm in the coroutine!")
- yield()
- say("I'm back in the coroutine!")
- )
- >> co
- say("I'm in the main func")
- >> co.resume()
- say("I'm back in the main func")
- >> co.resume()
- say("I'm back in the main func again")
- >> co.resume()
-```
diff --git a/examples/coroutines/aco.c b/examples/coroutines/aco.c
deleted file mode 100644
index 258efe28..00000000
--- a/examples/coroutines/aco.c
+++ /dev/null
@@ -1,458 +0,0 @@
-// Copyright 2018 Sen Han <00hnes@gmail.com>
-// Modifications copyright 2025 Bruce Hill <bruce@bruce-hill.com>
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#define _GNU_SOURCE
-
-#include "aco.h"
-#include <stdint.h>
-#include <stdio.h>
-
-#ifndef public
-#define public __attribute__((visibility("default")))
-#endif
-
-#define aco_size_t_safe_add_assert(a, b) aco_assert((a) + (b) >= (a))
-
-static void aco_default_protector_last_word(void *);
-
-void *(*aco_alloc_fn)(size_t) = malloc;
-void (*aco_dealloc_fn)(void *) = free;
-
-#define aco_alloc(size) \
- ({ \
- void *_ptr = aco_alloc_fn(size); \
- if (aco_unlikely((_ptr) == NULL)) { \
- fprintf(stderr, "Aborting: failed to allocate memory: %s:%d:%s\n", __FILE__, __LINE__, \
- __PRETTY_FUNCTION__); \
- abort(); \
- } \
- _ptr; \
- })
-
-// aco's Global Thread Local Storage variable `co`
-public
-__thread aco_t *aco_gtls_co;
-static __thread aco_cofuncp_t aco_gtls_last_word_fp = aco_default_protector_last_word;
-
-#ifdef __i386__
-static __thread void *aco_gtls_fpucw_mxcsr[2];
-#elif __x86_64__
-static __thread void *aco_gtls_fpucw_mxcsr[1];
-#else
-#error "platform not supporteded yet"
-#endif
-
-public
-void aco_runtime_test(void) {
-#ifdef __i386__
- _Static_assert(sizeof(void *) == 4, "require 'sizeof(void*) == 4'");
-#elif __x86_64__
- _Static_assert(sizeof(void *) == 8, "require 'sizeof(void*) == 8'");
- _Static_assert(sizeof(__uint128_t) == 16, "require 'sizeof(__uint128_t) == 16'");
-#else
-#error "platform not supporteded yet"
-#endif
- _Static_assert(sizeof(int) >= 4, "require 'sizeof(int) >= 4'");
- aco_assert(sizeof(int) >= 4);
- _Static_assert(sizeof(int) <= sizeof(size_t), "require 'sizeof(int) <= sizeof(size_t)'");
- aco_assert(sizeof(int) <= sizeof(size_t));
-}
-
-#ifdef __x86_64__
-static inline void aco_fast_memcpy(void *dst, const void *src, size_t sz) {
- if (((uintptr_t)src & 0x0f) != 0 || ((uintptr_t)dst & 0x0f) != 0 || (sz & 0x0f) != 0x08 || (sz >> 4) > 8) {
- memcpy(dst, src, sz);
- return;
- }
-
- __uint128_t xmm0, xmm1, xmm2, xmm3, xmm4, xmm5, xmm6, xmm7;
- switch (sz >> 4) {
- case 0: break;
- case 1:
- xmm0 = *((__uint128_t *)src + 0);
- *((__uint128_t *)dst + 0) = xmm0;
- break;
- case 2:
- xmm0 = *((__uint128_t *)src + 0);
- xmm1 = *((__uint128_t *)src + 1);
- *((__uint128_t *)dst + 0) = xmm0;
- *((__uint128_t *)dst + 1) = xmm1;
- break;
- case 3:
- xmm0 = *((__uint128_t *)src + 0);
- xmm1 = *((__uint128_t *)src + 1);
- xmm2 = *((__uint128_t *)src + 2);
- *((__uint128_t *)dst + 0) = xmm0;
- *((__uint128_t *)dst + 1) = xmm1;
- *((__uint128_t *)dst + 2) = xmm2;
- break;
- case 4:
- xmm0 = *((__uint128_t *)src + 0);
- xmm1 = *((__uint128_t *)src + 1);
- xmm2 = *((__uint128_t *)src + 2);
- xmm3 = *((__uint128_t *)src + 3);
- *((__uint128_t *)dst + 0) = xmm0;
- *((__uint128_t *)dst + 1) = xmm1;
- *((__uint128_t *)dst + 2) = xmm2;
- *((__uint128_t *)dst + 3) = xmm3;
- break;
- case 5:
- xmm0 = *((__uint128_t *)src + 0);
- xmm1 = *((__uint128_t *)src + 1);
- xmm2 = *((__uint128_t *)src + 2);
- xmm3 = *((__uint128_t *)src + 3);
- xmm4 = *((__uint128_t *)src + 4);
- *((__uint128_t *)dst + 0) = xmm0;
- *((__uint128_t *)dst + 1) = xmm1;
- *((__uint128_t *)dst + 2) = xmm2;
- *((__uint128_t *)dst + 3) = xmm3;
- *((__uint128_t *)dst + 4) = xmm4;
- break;
- case 6:
- xmm0 = *((__uint128_t *)src + 0);
- xmm1 = *((__uint128_t *)src + 1);
- xmm2 = *((__uint128_t *)src + 2);
- xmm3 = *((__uint128_t *)src + 3);
- xmm4 = *((__uint128_t *)src + 4);
- xmm5 = *((__uint128_t *)src + 5);
- *((__uint128_t *)dst + 0) = xmm0;
- *((__uint128_t *)dst + 1) = xmm1;
- *((__uint128_t *)dst + 2) = xmm2;
- *((__uint128_t *)dst + 3) = xmm3;
- *((__uint128_t *)dst + 4) = xmm4;
- *((__uint128_t *)dst + 5) = xmm5;
- break;
- case 7:
- xmm0 = *((__uint128_t *)src + 0);
- xmm1 = *((__uint128_t *)src + 1);
- xmm2 = *((__uint128_t *)src + 2);
- xmm3 = *((__uint128_t *)src + 3);
- xmm4 = *((__uint128_t *)src + 4);
- xmm5 = *((__uint128_t *)src + 5);
- xmm6 = *((__uint128_t *)src + 6);
- *((__uint128_t *)dst + 0) = xmm0;
- *((__uint128_t *)dst + 1) = xmm1;
- *((__uint128_t *)dst + 2) = xmm2;
- *((__uint128_t *)dst + 3) = xmm3;
- *((__uint128_t *)dst + 4) = xmm4;
- *((__uint128_t *)dst + 5) = xmm5;
- *((__uint128_t *)dst + 6) = xmm6;
- break;
- case 8:
- xmm0 = *((__uint128_t *)src + 0);
- xmm1 = *((__uint128_t *)src + 1);
- xmm2 = *((__uint128_t *)src + 2);
- xmm3 = *((__uint128_t *)src + 3);
- xmm4 = *((__uint128_t *)src + 4);
- xmm5 = *((__uint128_t *)src + 5);
- xmm6 = *((__uint128_t *)src + 6);
- xmm7 = *((__uint128_t *)src + 7);
- *((__uint128_t *)dst + 0) = xmm0;
- *((__uint128_t *)dst + 1) = xmm1;
- *((__uint128_t *)dst + 2) = xmm2;
- *((__uint128_t *)dst + 3) = xmm3;
- *((__uint128_t *)dst + 4) = xmm4;
- *((__uint128_t *)dst + 5) = xmm5;
- *((__uint128_t *)dst + 6) = xmm6;
- *((__uint128_t *)dst + 7) = xmm7;
- break;
- }
- *((uint64_t *)((uintptr_t)dst + sz - 8)) = *((uint64_t *)((uintptr_t)src + sz - 8));
-}
-#endif
-
-void aco_default_protector_last_word(void *_) {
- aco_t *co = aco_get_co();
- // do some log about the offending `co`
- fprintf(stderr, "error: aco_default_protector_last_word triggered\n");
- fprintf(stderr,
- "error: co:%p should call `aco_exit()` instead of direct "
- "`return` in co_fp:%p to finish its execution\n",
- co, (void *)co->fp);
- aco_assert(0);
-}
-
-public
-void aco_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *)) {
- aco_alloc_fn = alloc;
- aco_dealloc_fn = dealloc;
-}
-
-public
-void aco_thread_init(aco_cofuncp_t last_word_co_fp) {
- aco_save_fpucw_mxcsr(aco_gtls_fpucw_mxcsr);
-
- if ((void *)last_word_co_fp != NULL) aco_gtls_last_word_fp = last_word_co_fp;
-}
-
-// This function `aco_funcp_protector` should never be
-// called. If it's been called, that means the offending
-// `co` didn't call aco_exit(co) instead of `return` to
-// finish its execution.
-public
-void aco_funcp_protector(void) {
- if ((void *)(aco_gtls_last_word_fp) != NULL) {
- aco_gtls_last_word_fp(NULL);
- } else {
- aco_default_protector_last_word(NULL);
- }
- aco_assert(0);
-}
-
-public
-aco_shared_stack_t *aco_shared_stack_new(size_t sz) { return aco_shared_stack_new2(sz, 1); }
-
-public
-aco_shared_stack_t *aco_shared_stack_new2(size_t sz, bool guard_page_enabled) {
- if (sz == 0) {
- sz = 1024 * 1024 * 2;
- }
- if (sz < 4096) {
- sz = 4096;
- }
- aco_assert(sz > 0);
-
- size_t u_pgsz = 0;
- if (guard_page_enabled) {
- // although gcc's Built-in Functions to Perform Arithmetic with
- // Overflow Checking is better, but it would require gcc >= 5.0
- long pgsz = sysconf(_SC_PAGESIZE);
- // pgsz must be > 0 && a power of two
- aco_assert(pgsz > 0 && (((pgsz - 1) & pgsz) == 0));
- u_pgsz = (size_t)((unsigned long)pgsz);
- // it should be always true in real life
- aco_assert(u_pgsz == (unsigned long)pgsz && ((u_pgsz << 1) >> 1) == u_pgsz);
- if (sz <= u_pgsz) {
- sz = u_pgsz << 1;
- } else {
- size_t new_sz;
- if ((sz & (u_pgsz - 1)) != 0) {
- new_sz = (sz & (~(u_pgsz - 1)));
- aco_assert(new_sz >= u_pgsz);
- aco_size_t_safe_add_assert(new_sz, (u_pgsz << 1));
- new_sz = new_sz + (u_pgsz << 1);
- aco_assert(sz / u_pgsz + 2 == new_sz / u_pgsz);
- } else {
- aco_size_t_safe_add_assert(sz, u_pgsz);
- new_sz = sz + u_pgsz;
- aco_assert(sz / u_pgsz + 1 == new_sz / u_pgsz);
- }
- sz = new_sz;
- aco_assert((sz / u_pgsz > 1) && ((sz & (u_pgsz - 1)) == 0));
- }
- }
-
- aco_shared_stack_t *p = aco_alloc(sizeof(aco_shared_stack_t));
- memset(p, 0, sizeof(aco_shared_stack_t));
-
- if (guard_page_enabled) {
- p->real_ptr = mmap(NULL, sz, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
- if (aco_unlikely(p->real_ptr == MAP_FAILED)) {
- fprintf(stderr, "Aborting: failed to allocate memory: %s:%d:%s\n", __FILE__, __LINE__, __PRETTY_FUNCTION__);
- abort();
- }
- p->guard_page_enabled = true;
- aco_assert(0 == mprotect(p->real_ptr, u_pgsz, PROT_READ));
-
- p->ptr = (void *)(((uintptr_t)p->real_ptr) + u_pgsz);
- p->real_sz = sz;
- aco_assert(sz >= (u_pgsz << 1));
- p->sz = sz - u_pgsz;
- } else {
- // p->guard_page_enabled = 0;
- p->sz = sz;
- p->ptr = aco_alloc(sz);
- }
-
- p->owner = NULL;
-#ifdef ACO_USE_VALGRIND
- p->valgrind_stk_id = VALGRIND_STACK_REGISTER(p->ptr, (void *)((uintptr_t)p->ptr + p->sz));
-#endif
-#if defined(__i386__) || defined(__x86_64__)
- uintptr_t u_p = (uintptr_t)(p->sz - (sizeof(void *) << 1) + (uintptr_t)p->ptr);
- u_p = (u_p >> 4) << 4;
- p->align_highptr = (void *)u_p;
- p->align_retptr = (void *)(u_p - sizeof(void *));
- *((void **)(p->align_retptr)) = (void *)(aco_funcp_protector_asm);
- aco_assert(p->sz > (16 + (sizeof(void *) << 1) + sizeof(void *)));
- p->align_limit = p->sz - 16 - (sizeof(void *) << 1);
-#else
-#error "platform not supporteded yet"
-#endif
- return p;
-}
-
-public
-void aco_shared_stack_destroy(aco_shared_stack_t *sstk) {
- aco_assert(sstk != NULL && sstk->ptr != NULL);
-#ifdef ACO_USE_VALGRIND
- VALGRIND_STACK_DEREGISTER(sstk->valgrind_stk_id);
-#endif
- if (sstk->guard_page_enabled) {
- aco_assert(0 == munmap(sstk->real_ptr, sstk->real_sz));
- sstk->real_ptr = NULL;
- sstk->ptr = NULL;
- } else {
- if (aco_dealloc_fn != NULL) aco_dealloc_fn(sstk->ptr);
- sstk->ptr = NULL;
- }
- if (aco_dealloc_fn != NULL) aco_dealloc_fn(sstk);
-}
-
-public
-aco_t *aco_create(aco_t *main_co, aco_shared_stack_t *shared_stack, size_t saved_stack_sz, aco_cofuncp_t fp,
- void *arg) {
- aco_t *p = aco_alloc(sizeof(aco_t));
- memset(p, 0, sizeof(aco_t));
-
- if (main_co != NULL) { // non-main co
- aco_assertptr(shared_stack);
- p->shared_stack = shared_stack;
-#ifdef __i386__
- // POSIX.1-2008 (IEEE Std 1003.1-2008) - General Information - Data Types - Pointer Types
- // http://pubs.opengroup.org/onlinepubs/9699919799.2008edition/functions/V2_chap02.html#tag_15_12_03
- p->reg[ACO_REG_IDX_RETADDR] = (void *)fp;
- // push retaddr
- p->reg[ACO_REG_IDX_SP] = p->shared_stack->align_retptr;
-#ifndef ACO_CONFIG_SHARE_FPU_MXCSR_ENV
- p->reg[ACO_REG_IDX_FPU] = aco_gtls_fpucw_mxcsr[0];
- p->reg[ACO_REG_IDX_FPU + 1] = aco_gtls_fpucw_mxcsr[1];
-#endif
-#elif __x86_64__
- p->reg[ACO_REG_IDX_RETADDR] = (void *)fp;
- p->reg[ACO_REG_IDX_SP] = p->shared_stack->align_retptr;
-#ifndef ACO_CONFIG_SHARE_FPU_MXCSR_ENV
- p->reg[ACO_REG_IDX_FPU] = aco_gtls_fpucw_mxcsr[0];
-#endif
-#else
-#error "platform not supporteded yet"
-#endif
- p->main_co = main_co;
- p->arg = arg;
- p->fp = fp;
- if (saved_stack_sz == 0) {
- saved_stack_sz = 64;
- }
- p->saved_stack.ptr = aco_alloc(saved_stack_sz);
- p->saved_stack.sz = saved_stack_sz;
-#if defined(__i386__) || defined(__x86_64__)
- p->saved_stack.valid_sz = 0;
-#else
-#error "platform not supporteded yet"
-#endif
- return p;
- } else { // main co
- p->main_co = NULL;
- p->arg = arg;
- p->fp = fp;
- p->shared_stack = NULL;
- p->saved_stack.ptr = NULL;
- return p;
- }
- aco_assert(0);
-}
-
-public
-aco_attr_no_asan void aco_resume(aco_t *resume_co) {
- aco_assert(resume_co != NULL && resume_co->main_co != NULL && !resume_co->is_finished);
- if (resume_co->shared_stack->owner != resume_co) {
- if (resume_co->shared_stack->owner != NULL) {
- aco_t *owner_co = resume_co->shared_stack->owner;
- aco_assert(owner_co->shared_stack == resume_co->shared_stack);
-#if defined(__i386__) || defined(__x86_64__)
- aco_assert(((uintptr_t)(owner_co->shared_stack->align_retptr) >= (uintptr_t)(owner_co->reg[ACO_REG_IDX_SP]))
- && ((uintptr_t)(owner_co->shared_stack->align_highptr)
- - (uintptr_t)(owner_co->shared_stack->align_limit)
- <= (uintptr_t)(owner_co->reg[ACO_REG_IDX_SP])));
- owner_co->saved_stack.valid_sz =
- (uintptr_t)(owner_co->shared_stack->align_retptr) - (uintptr_t)(owner_co->reg[ACO_REG_IDX_SP]);
- if (owner_co->saved_stack.sz < owner_co->saved_stack.valid_sz) {
- if (aco_dealloc_fn != NULL) aco_dealloc_fn(owner_co->saved_stack.ptr);
- owner_co->saved_stack.ptr = NULL;
- while (1) {
- owner_co->saved_stack.sz = owner_co->saved_stack.sz << 1;
- aco_assert(owner_co->saved_stack.sz > 0);
- if (owner_co->saved_stack.sz >= owner_co->saved_stack.valid_sz) {
- break;
- }
- }
- owner_co->saved_stack.ptr = aco_alloc(owner_co->saved_stack.sz);
- }
- // TODO: optimize the performance penalty of memcpy function call
- // for very short memory span
- if (owner_co->saved_stack.valid_sz > 0) {
-#ifdef __x86_64__
- aco_fast_memcpy(owner_co->saved_stack.ptr, owner_co->reg[ACO_REG_IDX_SP],
- owner_co->saved_stack.valid_sz);
-#else
- memcpy(owner_co->saved_stack.ptr, owner_co->reg[ACO_REG_IDX_SP], owner_co->saved_stack.valid_sz);
-#endif
- owner_co->saved_stack.ct_save++;
- }
- if (owner_co->saved_stack.valid_sz > owner_co->saved_stack.max_cpsz) {
- owner_co->saved_stack.max_cpsz = owner_co->saved_stack.valid_sz;
- }
- owner_co->shared_stack->owner = NULL;
- owner_co->shared_stack->align_validsz = 0;
-#else
-#error "platform not supporteded yet"
-#endif
- }
- aco_assert(resume_co->shared_stack->owner == NULL);
-#if defined(__i386__) || defined(__x86_64__)
- aco_assert(resume_co->saved_stack.valid_sz <= resume_co->shared_stack->align_limit - sizeof(void *));
- // TODO: optimize the performance penalty of memcpy function call
- // for very short memory span
- if (resume_co->saved_stack.valid_sz > 0) {
- void *dst = (void *)((uintptr_t)(resume_co->shared_stack->align_retptr) - resume_co->saved_stack.valid_sz);
-#ifdef __x86_64__
- aco_fast_memcpy(dst, resume_co->saved_stack.ptr, resume_co->saved_stack.valid_sz);
-#else
- memcpy(dst, resume_co->saved_stack.ptr, resume_co->saved_stack.valid_sz);
-#endif
- resume_co->saved_stack.ct_restore++;
- }
- if (resume_co->saved_stack.valid_sz > resume_co->saved_stack.max_cpsz) {
- resume_co->saved_stack.max_cpsz = resume_co->saved_stack.valid_sz;
- }
- resume_co->shared_stack->align_validsz = resume_co->saved_stack.valid_sz + sizeof(void *);
- resume_co->shared_stack->owner = resume_co;
-#else
-#error "platform not supporteded yet"
-#endif
- }
- aco_gtls_co = resume_co;
- aco_yield_asm(resume_co->main_co, resume_co);
- aco_gtls_co = resume_co->main_co;
-}
-
-public
-void aco_destroy(aco_t *co) {
- aco_assertptr(co);
- if (aco_is_main_co(co)) {
- if (aco_dealloc_fn != NULL) aco_dealloc_fn(co);
- } else {
- if (co->shared_stack->owner == co) {
- co->shared_stack->owner = NULL;
- co->shared_stack->align_validsz = 0;
- }
- if (aco_dealloc_fn != NULL) aco_dealloc_fn(co->saved_stack.ptr);
- co->saved_stack.ptr = NULL;
- if (aco_dealloc_fn != NULL) aco_dealloc_fn(co);
- }
-}
-
-public
-void aco_exit_fn(void *_) { aco_exit(); }
diff --git a/examples/coroutines/aco.h b/examples/coroutines/aco.h
deleted file mode 100644
index 05ba0bdb..00000000
--- a/examples/coroutines/aco.h
+++ /dev/null
@@ -1,213 +0,0 @@
-// A coroutine library
-// Copyright 2018 Sen Han <00hnes@gmail.com>
-// Modifications copyright 2025 Bruce Hill <bruce@bruce-hill.com>
-//
-// Licensed under the Apache License, Version 2.0 (the "License");
-// you may not use this file except in compliance with the License.
-// You may obtain a copy of the License at
-//
-// http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing, software
-// distributed under the License is distributed on an "AS IS" BASIS,
-// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-// See the License for the specific language governing permissions and
-// limitations under the License.
-
-#pragma once
-
-#include <limits.h>
-#include <stdbool.h>
-#include <stdint.h>
-#include <stdio.h>
-#include <stdlib.h>
-#include <string.h>
-#include <sys/mman.h>
-#include <time.h>
-#include <unistd.h>
-
-#ifdef ACO_USE_VALGRIND
-#include <valgrind/valgrind.h>
-#endif
-
-#ifdef __cplusplus
-extern "C" {
-#endif
-
-#define ACO_VERSION_MAJOR 2
-#define ACO_VERSION_MINOR 0
-#define ACO_VERSION_PATCH 0
-
-#ifdef __i386__
-#define ACO_REG_IDX_RETADDR 0
-#define ACO_REG_IDX_SP 1
-#define ACO_REG_IDX_BP 2
-#define ACO_REG_IDX_ARG1 0
-#define ACO_REG_IDX_FPU 6
-#elif __x86_64__
-#define ACO_REG_IDX_RETADDR 4
-#define ACO_REG_IDX_SP 5
-#define ACO_REG_IDX_BP 7
-#define ACO_REG_IDX_EDI 8
-#define ACO_REG_IDX_FPU 8
-#else
-#error "platform not supported yet"
-#endif
-
-typedef struct {
- void *ptr;
- size_t sz;
- size_t valid_sz;
- // max copy size in bytes
- size_t max_cpsz;
- // copy from shared stack to this saved stack
- size_t ct_save;
- // copy from this saved stack to shared stack
- size_t ct_restore;
-} aco_saved_stack_t;
-
-struct aco_s;
-typedef struct aco_s aco_t;
-
-typedef struct {
- void *ptr;
- size_t sz;
- void *align_highptr;
- void *align_retptr;
- size_t align_validsz;
- size_t align_limit;
- aco_t *owner;
-
- bool guard_page_enabled;
- void *real_ptr;
- size_t real_sz;
-
-#ifdef ACO_USE_VALGRIND
- unsigned long valgrind_stk_id;
-#endif
-} aco_shared_stack_t;
-
-typedef void (*aco_cofuncp_t)(void *);
-
-struct aco_s {
- // cpu registers' state
-#ifdef __i386__
-#ifdef ACO_CONFIG_SHARE_FPU_MXCSR_ENV
- void *reg[6];
-#else
- void *reg[8];
-#endif
-#elif __x86_64__
-#ifdef ACO_CONFIG_SHARE_FPU_MXCSR_ENV
- void *reg[8];
-#else
- void *reg[9];
-#endif
-#else
-#error "platform not supported yet"
-#endif
- aco_t *main_co;
- void *arg;
- bool is_finished;
-
- aco_cofuncp_t fp;
-
- aco_saved_stack_t saved_stack;
- aco_shared_stack_t *shared_stack;
-};
-
-#define aco_likely(x) (__builtin_expect(!!(x), 1))
-
-#define aco_unlikely(x) (__builtin_expect(!!(x), 0))
-
-#define aco_assert(EX) ((aco_likely(EX)) ? ((void)0) : (abort()))
-
-#define aco_assertptr(ptr) ((aco_likely((ptr) != NULL)) ? ((void)0) : (abort()))
-
-#if defined(aco_attr_no_asan)
-#error "aco_attr_no_asan already defined"
-#endif
-#if defined(ACO_USE_ASAN)
-#if defined(__has_feature)
-#if __has_feature(__address_sanitizer__)
-#define aco_attr_no_asan __attribute__((__no_sanitize_address__))
-#endif
-#endif
-#if defined(__SANITIZE_ADDRESS__) && !defined(aco_attr_no_asan)
-#define aco_attr_no_asan __attribute__((__no_sanitize_address__))
-#endif
-#endif
-#ifndef aco_attr_no_asan
-#define aco_attr_no_asan
-#endif
-
-void aco_runtime_test(void);
-
-void aco_set_allocator(void *(*alloc)(size_t), void (*dealloc)(void *));
-
-void aco_thread_init(aco_cofuncp_t last_word_co_fp);
-
-void aco_yield_asm(aco_t *from_co, aco_t *to_co) __asm__("aco_yield_asm"); // asm
-
-void aco_save_fpucw_mxcsr(void *p) __asm__("aco_save_fpucw_mxcsr"); // asm
-
-void aco_funcp_protector_asm(void) __asm__("aco_funcp_protector_asm"); // asm
-
-void aco_funcp_protector(void);
-
-aco_shared_stack_t *aco_shared_stack_new(size_t sz);
-
-aco_shared_stack_t *aco_shared_stack_new2(size_t sz, bool guard_page_enabled);
-
-void aco_shared_stack_destroy(aco_shared_stack_t *sstk);
-
-aco_t *aco_create(aco_t *main_co, aco_shared_stack_t *shared_stack, size_t saved_stack_sz, aco_cofuncp_t fp, void *arg);
-
-// aco's Global Thread Local Storage variable `co`
-#ifdef __TINYC__
-#error "TinyCC doesn't support thread-local storage!"
-#else
-extern __thread aco_t *aco_gtls_co;
-#endif
-
-aco_attr_no_asan void aco_resume(aco_t *resume_co);
-
-// void aco_yield1(aco_t* yield_co);
-#define aco_yield1(yield_co) \
- do { \
- aco_assertptr((yield_co)); \
- aco_assertptr((yield_co)->main_co); \
- aco_yield_asm((yield_co), (yield_co)->main_co); \
- } while (0)
-
-#define aco_yield() aco_yield1(aco_gtls_co)
-
-#define aco_get_arg() (aco_gtls_co->arg)
-
-#define aco_get_co() \
- ({ \
- (void)0; \
- aco_gtls_co; \
- })
-
-void aco_destroy(aco_t *co);
-
-#define aco_is_main_co(co) ({ ((co)->main_co) == NULL; })
-
-#define aco_exit1(co) \
- do { \
- (co)->is_finished = true; \
- aco_assert((co)->shared_stack->owner == (co)); \
- (co)->shared_stack->owner = NULL; \
- (co)->shared_stack->align_validsz = 0; \
- aco_yield1((co)); \
- aco_assert(0); \
- } while (0)
-
-#define aco_exit() aco_exit1(aco_gtls_co)
-
-void aco_exit_fn(void *);
-
-#ifdef __cplusplus
-}
-#endif
diff --git a/examples/coroutines/acoyield.S b/examples/coroutines/acoyield.S
deleted file mode 100644
index 7bc87ff1..00000000
--- a/examples/coroutines/acoyield.S
+++ /dev/null
@@ -1,208 +0,0 @@
-.text
-.globl aco_yield_asm
-#if defined(__APPLE__)
-#else
-.type aco_yield_asm, @function
-#endif
-.intel_syntax noprefix
-aco_yield_asm:
-/*
- extern void aco_yield_asm(aco_t* from_co, aco_t* to_co);
-
- struct aco_t {
- void* reg[X];
- // ...
- }
-
- reference:
- https://github.com/hjl-tools/x86-psABI/wiki/X86-psABI
-
- pitfall:
- http://man7.org/linux/man-pages/man7/signal.7.html
- http://man7.org/linux/man-pages/man2/sigaltstack.2.html
-
- > $ man 7 signal
- > ...
- > By default, the signal handler is invoked on the normal process
- > stack. It is possible to arrange that the signal handler
- > uses an alternate stack; see sigaltstack(2) for a discussion of
- > how to do this and when it might be useful.
- > ...
-
- This is a BUG example:
- https://github.com/Tencent/libco/blob/v1.0/coctx_swap.S#L27
-
- proof of correctness:
- https://github.com/hnes/libaco
-
- mxcsr & fpu:
- fnstcw * m2byte
- Store FPU control word to m2byte without checking for
- pending unmasked floating-point exceptions.
-
- fldcw m2byte
- Load FPU control word from m2byte.
-
- stmxcsr m32
- Store contents of MXCSR register to m32
-
- ldmxcsr m32
- Load MXCSR register from m32.
-*/
-/*
- 0x00 --> 0xff
- eip esp ebp edi esi ebx fpucw16 mxcsr32
- 0 4 8 c 10 14 18 1c
-*/
-#ifdef __i386__
- mov eax,DWORD PTR [esp+0x4] // from_co
- mov edx,DWORD PTR [esp] // retaddr
- lea ecx,[esp+0x4] // esp
- mov DWORD PTR [eax+0x8],ebp //<ebp
- mov DWORD PTR [eax+0x4],ecx //<esp
- mov DWORD PTR [eax+0x0],edx //<retaddr
- mov DWORD PTR [eax+0xc],edi //<edi
- mov ecx,DWORD PTR [esp+0x8] // to_co
- mov DWORD PTR [eax+0x10],esi //<esi
- mov DWORD PTR [eax+0x14],ebx //<ebx
-#ifndef ACO_CONFIG_SHARE_FPU_MXCSR_ENV
- fnstcw WORD PTR [eax+0x18] //<fpucw
- stmxcsr DWORD PTR [eax+0x1c] //<mxcsr
-#endif
- mov edx,DWORD PTR [ecx+0x4] //>esp
- mov ebp,DWORD PTR [ecx+0x8] //>ebp
- mov eax,DWORD PTR [ecx+0x0] //>retaddr
- mov edi,DWORD PTR [ecx+0xc] //>edi
- mov esi,DWORD PTR [ecx+0x10] //>esi
- mov ebx,DWORD PTR [ecx+0x14] //>ebx
-#ifndef ACO_CONFIG_SHARE_FPU_MXCSR_ENV
- fldcw WORD PTR [ecx+0x18] //>fpucw
- ldmxcsr DWORD PTR [ecx+0x1c] //>mxcsr
-#endif
- xor ecx,ecx
- mov esp,edx
- mov edx,eax
-
- // Pass the user-provided argument as first argument:
-#ifdef ACO_CONFIG_SHARE_FPU_MXCSR_ENV
- mov eax,DWORD PTR [ecx+0x24]
-#else
- mov eax,DWORD PTR [ecx+0x28]
-#endif
-
- jmp edx
-#elif __x86_64__
-/*
- 0x00 --> 0xff
- r12 r13 r14 r15 rip rsp rbx rbp fpucw16 mxcsr32
- 0 8 10 18 20 28 30 38 40 44
-*/
- // rdi - from_co | rsi - to_co
- mov rdx,QWORD PTR [rsp] // retaddr
- lea rcx,[rsp+0x8] // rsp
- mov QWORD PTR [rdi+0x0], r12
- mov QWORD PTR [rdi+0x8], r13
- mov QWORD PTR [rdi+0x10],r14
- mov QWORD PTR [rdi+0x18],r15
- mov QWORD PTR [rdi+0x20],rdx // retaddr
- mov QWORD PTR [rdi+0x28],rcx // rsp
- mov QWORD PTR [rdi+0x30],rbx
- mov QWORD PTR [rdi+0x38],rbp
-#ifndef ACO_CONFIG_SHARE_FPU_MXCSR_ENV
- fnstcw WORD PTR [rdi+0x40]
- stmxcsr DWORD PTR [rdi+0x44]
-#endif
- mov r12,QWORD PTR [rsi+0x0]
- mov r13,QWORD PTR [rsi+0x8]
- mov r14,QWORD PTR [rsi+0x10]
- mov r15,QWORD PTR [rsi+0x18]
- mov rax,QWORD PTR [rsi+0x20] // retaddr
- mov rcx,QWORD PTR [rsi+0x28] // rsp
- mov rbx,QWORD PTR [rsi+0x30]
- mov rbp,QWORD PTR [rsi+0x38]
-#ifndef ACO_CONFIG_SHARE_FPU_MXCSR_ENV
- fldcw WORD PTR [rsi+0x40]
- ldmxcsr DWORD PTR [rsi+0x44]
-#endif
-
- // Pass the user-provided argument as first argument:
-#ifdef ACO_CONFIG_SHARE_FPU_MXCSR_ENV
- mov rdi,QWORD PTR [rsi+0x48]
-#else
- mov rdi,QWORD PTR [rsi+0x50]
-#endif
-
- mov rsp,rcx
- jmp rax
-#else
- #error "platform not supported"
-#endif
-
-.globl aco_save_fpucw_mxcsr
-#if defined(__APPLE__)
-#else
-.type aco_save_fpucw_mxcsr, @function
-#endif
-.intel_syntax noprefix
-aco_save_fpucw_mxcsr:
-#ifdef __i386__
- mov eax,DWORD PTR [esp+0x4] // ptr
- fnstcw WORD PTR [eax]
- stmxcsr DWORD PTR [eax+0x4]
- ret
-#elif __x86_64__
- fnstcw WORD PTR [rdi]
- stmxcsr DWORD PTR [rdi+0x4]
- ret
-#else
- #error "platform not supported"
-#endif
-
-#if defined(__APPLE__)
-.globl _abort
-.globl _aco_funcp_protector
-#else
-.globl abort
-.globl aco_funcp_protector
-#endif
-
-.globl aco_funcp_protector_asm
-#if defined(__APPLE__)
-#else
-.type aco_funcp_protector_asm, @function
-#endif
-.intel_syntax noprefix
-aco_funcp_protector_asm:
-#ifdef __i386__
- and esp,0xfffffff0
- #if defined(__APPLE__)
- call _aco_funcp_protector
- call _abort
- #else
- #if defined(__pic__) || defined(__PIC__)
- call aco_funcp_protector@PLT
- call abort@PLT
- #else
- call aco_funcp_protector
- call abort
- #endif
- #endif
- ret
-#elif __x86_64__
- and rsp,0xfffffffffffffff0
- #if defined(__APPLE__)
- call _aco_funcp_protector
- call _abort
- #else
- #if defined(__pic__) || defined(__PIC__)
- call aco_funcp_protector@PLT
- call abort@PLT
- #else
- call aco_funcp_protector
- call abort
- #endif
- #endif
- ret
-#else
- #error "platform not supported"
-#endif
diff --git a/examples/coroutines/coroutines.tm b/examples/coroutines/coroutines.tm
deleted file mode 100644
index b530a685..00000000
--- a/examples/coroutines/coroutines.tm
+++ /dev/null
@@ -1,67 +0,0 @@
-# This is a coroutine library that uses libaco (https://libaco.org)
-#
-# Lua programmers will recognize this as similar to Lua's stackful coroutines.
-#
-# Async/Await programmers will weep at its beauty and gnash their teeth and
-# rend their garments in despair at what they could have had.
-
-use ./aco.h
-use ./aco.c
-use ./acoyield.S
-
-func main()
- say("Example usage")
- co := Coroutine(func()
- say("I'm in the coroutine!")
- yield()
- say("I'm back in the coroutine!")
- )
- >> co
- say("I'm in the main func")
- >> co.resume()
- say("I'm back in the main func")
- >> co.resume()
- say("I'm back in the main func again")
- >> co.resume()
-
-struct aco_t(; extern, opaque)
-struct aco_shared_stack_t(; extern, opaque)
-
-_main_co : @aco_t? = none
-_shared_stack : @aco_shared_stack_t? = none
-
-struct Coroutine(co:@aco_t)
- convert(fn:func() -> Coroutine)
- if not _main_co
- _init()
-
- main_co := _main_co
- shared_stack := _shared_stack
- aco_ptr := C_code:@aco_t(
- aco_create(@main_co, @shared_stack, 0, (void*)@fn.fn, @fn.userdata)
- )
- return Coroutine(aco_ptr)
-
- func is_finished(co:Coroutine->Bool; inline)
- return C_code:Bool(((aco_t*)@co.co)->is_finished)
-
- func resume(co:Coroutine->Bool)
- if co.is_finished()
- return no
- C_code { aco_resume(@co.co); }
- return yes
-
-func _init()
- C_code {
- aco_set_allocator(GC_malloc, NULL);
- aco_thread_init(aco_exit_fn);
- }
- _main_co = C_code:@aco_t(aco_create(NULL, NULL, 0, NULL, NULL))
-
- _shared_stack = C_code:@aco_shared_stack_t(aco_shared_stack_new(0))
-
-func yield(; inline)
- C_code {
- aco_yield();
- }
-
diff --git a/examples/game/CHANGES.md b/examples/game/CHANGES.md
deleted file mode 100644
index 42ae752c..00000000
--- a/examples/game/CHANGES.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Version History
-
-## v1.0
-
-Initial version
diff --git a/examples/game/Makefile b/examples/game/Makefile
deleted file mode 100644
index 7cf46ce6..00000000
--- a/examples/game/Makefile
+++ /dev/null
@@ -1,17 +0,0 @@
-
-
-game: game.tm box.tm color.tm player.tm world.tm
- tomo -e game.tm
-
-# Disable built-in makefile rules:
-%: %.c
-%.o: %.c
-%: %.o
-
-clean:
- rm -vf game *.tm.*
-
-play: game
- ./game
-
-.PHONY: play, clean
diff --git a/examples/game/README.md b/examples/game/README.md
deleted file mode 100644
index 475a8299..00000000
--- a/examples/game/README.md
+++ /dev/null
@@ -1,13 +0,0 @@
-# Example Game
-
-This is a simple example game that uses [raylib](https://www.raylib.com/) to
-demonstrate a project that spans multiple files and showcases some game
-programming concepts used in idiomatic Tomo code. It also showcases how to
-interact with an external C library. To run the game:
-
-```bash
-tomo game.tm
-```
-
-An example [Makefile](Makefile) is also provided if you want to use `make` to
-build the game and `make clean` to clean up the built files.
diff --git a/examples/game/box.tm b/examples/game/box.tm
deleted file mode 100644
index 41ae10e5..00000000
--- a/examples/game/box.tm
+++ /dev/null
@@ -1,7 +0,0 @@
-# Defines a struct representing boxes on the terrain
-use ./world.tm
-use ./raylib.tm
-
-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
deleted file mode 100644
index f82e4f40..00000000
--- a/examples/game/game.tm
+++ /dev/null
@@ -1,29 +0,0 @@
-# This game demo uses Raylib to present a simple maze-type game
-use ./raylib.tm
-use ./world.tm
-
-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")
-
- world := @World(
- player=@Player(Vector2(0,0), Vector2(0,0)),
- goal=@Box(Vector2(0,0), Vector2(50,50), color=Color(0x10,0xa0,0x10)),
- boxes=@[],
- )
- world.load_map(map_contents)
-
- SetTargetFPS(60)
-
- while not WindowShouldClose()
- dt := GetFrameTime()
- world.update(dt)
-
- BeginDrawing()
- ClearBackground(Color(0xCC, 0xCC, 0xCC, 0xFF))
- world.draw()
- EndDrawing()
-
- CloseWindow()
-
diff --git a/examples/game/map.txt b/examples/game/map.txt
deleted file mode 100644
index 46fccd09..00000000
--- a/examples/game/map.txt
+++ /dev/null
@@ -1,18 +0,0 @@
-################################
-#@ # # #
-# #### #### # # #### #####
-# # # # # #
-# ####### # ####### # # ###
-# # # # # # #
-# #### ########## # #### #
-# # # # # # #
-#### # # # # # # # # ##
-# # # # # # # #
-####### ########## # ########
-# # # # #
-# ########## #### # # # #
-# # # # # # # # #
-# ####### # # ########## #
-# # # # # # # ? #
-# # # # # # #
-################################
diff --git a/examples/game/player.tm b/examples/game/player.tm
deleted file mode 100644
index 2e5e54f6..00000000
--- a/examples/game/player.tm
+++ /dev/null
@@ -1,28 +0,0 @@
-# Defines a struct representing the player, which is controlled by WASD keys
-use ./world.tm
-use ./raylib.tm
-
-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)
- target_x := C_code:Num32(
- (Num32_t)((IsKeyDown(KEY_A) ? -1 : 0) + (IsKeyDown(KEY_D) ? 1 : 0))
- )
- target_y := C_code:Num32(
- (Num32_t)((IsKeyDown(KEY_W) ? -1 : 0) + (IsKeyDown(KEY_S) ? 1 : 0))
- )
- target_vel := Vector2(target_x, target_y).norm() * Player.WALK_SPEED
-
- vel := (p.pos - p.prev_pos)/World.DT
- vel *= Player.FRICTION
- vel = vel.mix(target_vel, Player.ACCEL)
-
- p.prev_pos, p.pos = p.pos, p.pos + World.DT*vel
-
- func draw(p:Player)
- DrawRectangleV(p.pos, Player.SIZE, Player.COLOR)
diff --git a/examples/game/raylib.tm b/examples/game/raylib.tm
deleted file mode 100644
index b2ba53d7..00000000
--- a/examples/game/raylib.tm
+++ /dev/null
@@ -1,63 +0,0 @@
-# Raylib wrapper for some functions and structs
-use -lraylib
-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)
- DrawRectangleRec(r, color)
-
-struct Vector2(x,y:Num32; extern)
- ZERO := Vector2(0, 0)
- func plus(a,b:Vector2->Vector2; inline)
- return Vector2(a.x+b.x, a.y+b.y)
- func minus(a,b:Vector2->Vector2; inline)
- return Vector2(a.x-b.x, a.y-b.y)
- func times(a,b:Vector2->Vector2; inline)
- return Vector2(a.x*b.x, a.y*b.y)
- func negative(v:Vector2->Vector2; inline)
- return Vector2(-v.x, -v.y)
- 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)
- return a.x*b.y - a.y*b.x
- 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)
- return Vector2(v.x/divisor, v.y/divisor)
- func length(v:Vector2->Num32; inline)
- return (v.x*v.x + v.y*v.y).sqrt()
- func dist(a,b:Vector2->Num32; inline)
- return a.minus(b).length()
- 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
- return v
- len := v.length()
- return Vector2(v.x/len, v.y/len)
- 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)
- return Vector2(
- amount.mix(a.x, b.x),
- amount.mix(a.y, b.y),
- )
-
-extern InitWindow : func(width:Int32, height:Int32, title:CString)
-extern SetTargetFPS : func(fps:Int32)
-extern WindowShouldClose : func(->Bool)
-extern GetFrameTime : func(->Num32)
-extern BeginDrawing : func()
-extern EndDrawing : func()
-extern CloseWindow : func()
-extern ClearBackground : func(color:Color)
-extern DrawRectangle : func(x,y,width,height:Int32, color:Color)
-extern DrawRectangleRec : func(rec:Rectangle, color:Color)
-extern DrawRectangleV : func(pos:Vector2, size:Vector2, color:Color)
-extern DrawText : func(text:CString, x,y:Int32, text_height:Int32, color:Color)
-extern GetScreenWidth : func(->Int32)
-extern GetScreenHeight : func(->Int32)
diff --git a/examples/game/world.tm b/examples/game/world.tm
deleted file mode 100644
index 0de8ea4b..00000000
--- a/examples/game/world.tm
+++ /dev/null
@@ -1,88 +0,0 @@
-# This file defines a World object for keeping track of everything, as well
-# as the collision logic.
-use ./player.tm
-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)
- a_left := a_pos.x
- a_right := a_pos.x + a_size.x
- a_top := a_pos.y
- a_bottom := a_pos.y + a_size.y
-
- b_left := b_pos.x
- b_right := b_pos.x + b_size.x
- b_top := b_pos.y
- b_bottom := b_pos.y + b_size.y
-
- # Calculate the overlap in each dimension
- overlap_x := (a_right _min_ b_right) - (a_left _max_ b_left)
- 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
- return Vector2(0, 0)
-
- 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
- return Vector2(overlap_x, 0)
- 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
- return Vector2(0, -overlap_y)
-
- return Vector2(0, 0)
-
-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)
- w.dt_accum += dt
- while w.dt_accum > 0
- w.update_once()
- w.dt_accum -= World.DT
-
- 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)
- w.won = yes
-
- # Resolve player overlapping with any 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
- b.draw()
- w.goal.draw()
- w.player.draw()
-
- 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("[]")
- map = map.translate({"[]"="#", "@ "="@", " "=" "})
- w.boxes = @[]
- box_size := Vector2(50., 50.)
- 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 == "@"
- 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 == "?"
- pos := Vector2((Num32(x)-1) * box_size.x, (Num32(y)-1) * box_size.y)
- w.goal.pos = pos
-
diff --git a/examples/hello.tm b/examples/hello.tm
new file mode 100644
index 00000000..09ee3ab4
--- /dev/null
+++ b/examples/hello.tm
@@ -0,0 +1,3 @@
+# A simple hello world program:
+func main()
+ say("Hello world!")
diff --git a/examples/http-server/CHANGES.md b/examples/http-server/CHANGES.md
deleted file mode 100644
index 42ae752c..00000000
--- a/examples/http-server/CHANGES.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Version History
-
-## v1.0
-
-Initial version
diff --git a/examples/http-server/README.md b/examples/http-server/README.md
deleted file mode 100644
index 78c8d793..00000000
--- a/examples/http-server/README.md
+++ /dev/null
@@ -1,8 +0,0 @@
-# HTTP Server
-
-This is a simple multithreaded Tomo HTTP server that can be run like this:
-
-```
-tomo -e http-server.tm
-./http-server ./sample-site
-```
diff --git a/examples/http-server/connection-queue.tm b/examples/http-server/connection-queue.tm
deleted file mode 100644
index c56069e1..00000000
--- a/examples/http-server/connection-queue.tm
+++ /dev/null
@@ -1,25 +0,0 @@
-use pthreads
-
-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)
- queue._mutex.lock()
- queue._connections.insert(connection)
- queue._mutex.unlock()
- queue._cond.signal()
-
-
- func dequeue(queue:ConnectionQueue -> Int32)
- conn : Int32?
-
- queue._mutex.lock()
-
- while queue._connections.length == 0
- queue._cond.wait(queue._mutex)
-
- conn = queue._connections.pop(1)
- queue._mutex.unlock()
- queue._cond.signal()
- return conn!
diff --git a/examples/http-server/http-server.tm b/examples/http-server/http-server.tm
deleted file mode 100644
index 8e8aff7e..00000000
--- a/examples/http-server/http-server.tm
+++ /dev/null
@@ -1,149 +0,0 @@
-#!/bin/env tomo
-
-# This file provides an HTTP server module and standalone executable
-
-use <stdio.h>
-use <stdlib.h>
-use <string.h>
-use <unistd.h>
-use <arpa/inet.h>
-use <err.h>
-
-use commands
-use pthreads
-use patterns
-
-use ./connection-queue.tm
-
-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
- connection := connections.dequeue()
- request_text := C_code:Text(
- Text_t request = EMPTY_TEXT;
- char buf[1024] = {};
- for (ssize_t n; (n = read(@connection, buf, sizeof(buf) - 1)) > 0; ) {
- buf[n] = 0;
- request = Text$concat(request, Text$from_strn(buf, n));
- if (request.length > 1000000 || strstr(buf, "\r\n\r\n"))
- break;
- }
- request
- )
-
- request := HTTPRequest.from_text(request_text) or skip
- response := handler(request).bytes()
- C_code {
- if (@response.stride != 1)
- List$compact(&@response, 1);
- write(@connection, @response.data, @response.length);
- close(@connection);
- }
- ))
-
-
- sock := C_code:Int32(
- int s = socket(AF_INET, SOCK_STREAM, 0);
- if (s < 0) err(1, "Couldn't connect to socket!");
-
- int opt = 1;
- if (setsockopt(s, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)) < 0)
- err(1, "Couldn't set socket option");
-
- struct sockaddr_in addr = {AF_INET, htons(@port), INADDR_ANY};
- if (bind(s, (struct sockaddr*)&addr, sizeof(addr)) < 0)
- err(1, "Couldn't bind to socket");
- if (listen(s, 8) < 0)
- err(1, "Couldn't listen on socket");
-
- s
- )
-
- repeat
- conn := C_code:Int32(accept(@sock, NULL, NULL))
- stop if conn < 0
- connections.enqueue(conn)
-
- say("Shutting down...")
- for w in workers
- w.cancel()
-
-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+ /}', '/')
- version := m[3]
- rest := m[-1].pattern_captures($Pat/{..}{2 crlf}{0+ ..}/) or return none
- headers := rest[1].split_pattern($Pat/{crlf}/)
- 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])
- body_bytes := r.body.bytes()
- extra_headers := (++: "$k: $v\r\n" for k,v in r.headers) or ""
- return "
- HTTP/1.1 $(r.status) OK\r
- Content-Length: $(body_bytes.length + 2)\r
- Content-Type: $(r.content_type)\r
- Connection: close\r
- $extra_headers
- \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()
- Command(Text(file)).get_output()!
- else
- file.read()!
- return HTTPResponse(body, content_type=_content_type(file))
- is Redirect(destination)
- return HTTPResponse("Found", 302, headers={"Location"=destination})
-
-func load_routes(directory:Path -> {Text=RouteEntry})
- routes : &{Text=RouteEntry}
- 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"
- canonical := server_path.without_suffix("index.html")
- routes[server_path] = Redirect(canonical)
- routes[canonical] = ServeFile(file)
- 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"
- canonical := server_path.without_suffix(".tm")
- routes[server_path] = Redirect(canonical)
- routes[canonical] = ServeFile(file)
- else
- routes[server_path] = ServeFile(file)
- return routes[]
-
-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]
- return handler.respond(request)
- else
- return HTTPResponse("Not found!", 404)
- )
-
diff --git a/examples/http-server/modules.ini b/examples/http-server/modules.ini
deleted file mode 100644
index 171e11d2..00000000
--- a/examples/http-server/modules.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[pthreads]
-version=v1.0
-
-[patterns]
-version=v1.0
-
-[commands]
-version=v1.0
diff --git a/examples/http-server/sample-site/foo.html b/examples/http-server/sample-site/foo.html
deleted file mode 100644
index 162a7146..00000000
--- a/examples/http-server/sample-site/foo.html
+++ /dev/null
@@ -1,6 +0,0 @@
-<!DOCTYPE HTML>
-<html>
- <body>
- This is the <b>foo</b> page.
- </body>
-</html>
diff --git a/examples/http-server/sample-site/hello.txt b/examples/http-server/sample-site/hello.txt
deleted file mode 100644
index e965047a..00000000
--- a/examples/http-server/sample-site/hello.txt
+++ /dev/null
@@ -1 +0,0 @@
-Hello
diff --git a/examples/http-server/sample-site/index.html b/examples/http-server/sample-site/index.html
deleted file mode 100644
index 8e1573bb..00000000
--- a/examples/http-server/sample-site/index.html
+++ /dev/null
@@ -1,16 +0,0 @@
-<!DOCTYPE HTML>
-<html>
- <head>
- <title>HTTP Example</title>
- <link rel="stylesheet" href="styles.css">
- </head>
- <body>
- <p>
- Hello <b>world!</b>
- </p>
-
- <p>
- Try going to <a href="/random">/random</a> or <a href="/foo">/foo</a> or <a href="/hello.txt">/hello.txt</a>
- </p>
- </body>
-</html>
diff --git a/examples/http-server/sample-site/random.tm b/examples/http-server/sample-site/random.tm
deleted file mode 100755
index 153ac2af..00000000
--- a/examples/http-server/sample-site/random.tm
+++ /dev/null
@@ -1,17 +0,0 @@
-#!/bin/env tomo
-use random
-
-func main()
- say("
- <!DOCTYPE HTML>
- <html>
- <head>
- <title>Random Number</title>
- <link rel="stylesheet" href="styles.css">
- </head>
- <body>
- <h1>Random Number</h1>
- Your random number is: $(random.int(1,100))
- </body>
- </html>
- ")
diff --git a/examples/http-server/sample-site/styles.css b/examples/http-server/sample-site/styles.css
deleted file mode 100644
index f15d25de..00000000
--- a/examples/http-server/sample-site/styles.css
+++ /dev/null
@@ -1,11 +0,0 @@
-body{
- margin:40px auto;
- max-width:650px;
- line-height:1.6;
- font-size:18px;
- color:#444;
- padding:0 10px;
-}
-h1,h2,h3{
- line-height:1.2
-}
diff --git a/examples/http/CHANGES.md b/examples/http/CHANGES.md
deleted file mode 100644
index 42ae752c..00000000
--- a/examples/http/CHANGES.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Version History
-
-## v1.0
-
-Initial version
diff --git a/examples/http/http.tm b/examples/http/http.tm
deleted file mode 100644
index 3fe41ae2..00000000
--- a/examples/http/http.tm
+++ /dev/null
@@ -1,111 +0,0 @@
-# A simple HTTP library built using CURL
-
-use -lcurl
-use <curl/curl.h>
-
-struct HTTPResponse(code:Int, body:Text)
-
-enum _Method(GET, POST, PUT, PATCH, DELETE)
-
-func _send(method:_Method, url:Text, data:Text?, headers:[Text]=[] -> HTTPResponse)
- chunks : @[Text]
- save_chunk := func(chunk:CString, size:Int64, n:Int64)
- chunks.insert(C_code:Text(Text$from_strn(@chunk, @size*@n)))
- return n*size
-
- C_code {
- CURL *curl = curl_easy_init();
- struct curl_slist *chunk = NULL;
- curl_easy_setopt(curl, CURLOPT_URL, @(CString(url)));
- curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, @save_chunk.fn);
- curl_easy_setopt(curl, CURLOPT_WRITEDATA, @save_chunk.userdata);
- }
-
- defer
- C_code {
- if (chunk)
- curl_slist_free_all(chunk);
- curl_easy_cleanup(curl);
- }
-
- when method is POST
- C_code {
- curl_easy_setopt(curl, CURLOPT_POST, 1L);
- }
- if posting := data
- C_code {
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, @(CString(posting)));
- }
- is PUT
- C_code {
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PUT");
- }
- if putting := data
- C_code {
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, @(CString(putting)));
- }
- is PATCH
- C_code {
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "PATCH");
- }
- if patching := data
- C_code {
- curl_easy_setopt(curl, CURLOPT_POSTFIELDS, @(CString(patching)));
- }
- is DELETE
- C_code {
- curl_easy_setopt(curl, CURLOPT_CUSTOMREQUEST, "DELETE");
- }
- else
- pass
-
- for header in headers
- C_code {
- chunk = curl_slist_append(chunk, @(CString(header)));
- }
-
- C_code {
- if (chunk)
- curl_easy_setopt(curl, CURLOPT_HTTPHEADER, chunk);
- }
-
- code := Int64(0)
- C_code {
- CURLcode res = curl_easy_perform(curl);
- if (res != CURLE_OK)
- fprintf(stderr, "curl_easy_perform() failed: %s\n", curl_easy_strerror(res));
-
- curl_easy_getinfo(curl, CURLINFO_RESPONSE_CODE, &@code);
- }
- return HTTPResponse(Int(code), "".join(chunks))
-
-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)
- return _send(POST, url, data, headers)
-
-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)
- return _send(PATCH, url, data, headers)
-
-func delete(url:Text, data:Text?=none, headers=["Content-Type: application/json", "Accept: application/json"] -> HTTPResponse)
- return _send(DELETE, url, data, headers)
-
-func main()
- say("GET:")
- say(get("https://httpbin.org/get").body)
- say("Waiting 1sec...")
- sleep(1)
- say("POST:")
- say(post("https://httpbin.org/post", `{"key": "value"}`).body)
- say("Waiting 1sec...")
- sleep(1)
- say("PUT:")
- say(put("https://httpbin.org/put", `{"key": "value"}`).body)
- say("Waiting 1sec...")
- sleep(1)
- say("DELETE:")
- say(delete("https://httpbin.org/delete").body)
diff --git a/examples/ini/CHANGES.md b/examples/ini/CHANGES.md
deleted file mode 100644
index 42ae752c..00000000
--- a/examples/ini/CHANGES.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Version History
-
-## v1.0
-
-Initial version
diff --git a/examples/ini/ini.tm b/examples/ini/ini.tm
deleted file mode 100644
index 4dc27725..00000000
--- a/examples/ini/ini.tm
+++ /dev/null
@@ -1,61 +0,0 @@
-
-use patterns
-
-_USAGE := "
- Usage: ini <filename> "[section[/key]]"
-"
-_HELP := "
- ini: A .ini config file reader tool.
- $_USAGE
-"
-
-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}
-
- # Line wraps:
- text = text.replace_pattern($Pat/\\{1 nl}{0+space}/, " ")
-
- for line in text.lines()
- line = line.trim()
- skip if line.starts_with(";") or line.starts_with("#")
- 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/{..}={..}/)
- 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?)
- keys := (key or "").split($|/|)
- if keys.length > 2
- exit("
- Too many arguments!
- $_USAGE
- ")
-
- data := parse_ini(path)
- if keys.length < 1 or keys[1] == '*'
- say("$data")
- return
-
- section := keys[1].lower()
- section_data := data[section] or exit("
- Invalid section name: \[31;1]$section\[]
- Valid names: \[1]$(", ".join([k.quoted() for k in data.keys]))\[]
- ")
- if keys.length < 2 or keys[2] == '*'
- say("$section_data")
- return
-
- section_key := keys[2].lower()
- value := section_data[section_key] or exit("
- Invalid key: \[31;1]$section_key\[]
- Valid keys: \[1]$(", ".join([s.quoted() for s in section_data.keys]))\[]
- ")
- say(value)
diff --git a/examples/ini/modules.ini b/examples/ini/modules.ini
deleted file mode 100644
index fb52a859..00000000
--- a/examples/ini/modules.ini
+++ /dev/null
@@ -1,2 +0,0 @@
-[patterns]
-version=v1.0
diff --git a/examples/ini/test.ini b/examples/ini/test.ini
deleted file mode 100644
index 782dc76f..00000000
--- a/examples/ini/test.ini
+++ /dev/null
@@ -1,8 +0,0 @@
-[ Book ]
-title = Dirk Gently's Holistic Detective Agency
-author = Douglas Adams
-published = 1987
-
-[ Protagonist ]
-name = Dirk Gently
-age = 42
diff --git a/examples/log/CHANGES.md b/examples/log/CHANGES.md
deleted file mode 100644
index 42ae752c..00000000
--- a/examples/log/CHANGES.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Version History
-
-## v1.0
-
-Initial version
diff --git a/examples/log/log.tm b/examples/log/log.tm
deleted file mode 100644
index 4b7893fd..00000000
--- a/examples/log/log.tm
+++ /dev/null
@@ -1,50 +0,0 @@
-use <time.h>
-use <stdio.h>
-
-timestamp_format := CString("%F %T")
-
-logfiles : @|Path|
-
-func _timestamp(->Text)
- c_str := C_code:CString(
- char *str = GC_MALLOC_ATOMIC(20);
- time_t t; time(&t);
- struct tm *tm_info = localtime(&t);
- strftime(str, 20, "%F %T", tm_info);
- str
- )
- return c_str.as_text()
-
-func info(text:Text, newline=yes)
- say("\[2]⚫ $text\[]", newline)
- for file in logfiles
- file.append("$(_timestamp()) [info] $text\n")
-
-func debug(text:Text, newline=yes)
- say("\[32]🟢 $text\[]", newline)
- for file in logfiles
- file.append("$(_timestamp()) [debug] $text\n")
-
-func warn(text:Text, newline=yes)
- say("\[33;1]🟡 $text\[]", newline)
- for file in logfiles
- file.append("$(_timestamp()) [warn] $text\n")
-
-func error(text:Text, newline=yes)
- say("\[31;1]🔴 $text\[]", newline)
- for file in logfiles
- file.append("$(_timestamp()) [error] $text\n")
-
-func add_logfile(file:Path)
- logfiles.add(file)
-
-func remove_logfile(file:Path)
- logfiles.remove(file)
-
-func main()
- add_logfile((./log.txt))
- >> info("Hello")
- >> debug("Hello")
- >> warn("Hello")
- >> error("Hello")
-
diff --git a/examples/vectors/CHANGES.md b/examples/vectors/CHANGES.md
deleted file mode 100644
index 42ae752c..00000000
--- a/examples/vectors/CHANGES.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Version History
-
-## v1.0
-
-Initial version
diff --git a/examples/vectors/vectors.tm b/examples/vectors/vectors.tm
deleted file mode 100644
index 1fd0c4fa..00000000
--- a/examples/vectors/vectors.tm
+++ /dev/null
@@ -1,136 +0,0 @@
-# A math vector library for 2D and 3D vectors of Nums or Ints
-
-struct Vec2(x,y:Num)
- ZERO := Vec2(0, 0)
- func plus(a,b:Vec2->Vec2; inline)
- return Vec2(a.x+b.x, a.y+b.y)
- func minus(a,b:Vec2->Vec2; inline)
- return Vec2(a.x-b.x, a.y-b.y)
- func times(a,b:Vec2->Vec2; inline)
- return Vec2(a.x*b.x, a.y*b.y)
- func negative(v:Vec2->Vec2; inline)
- return Vec2(-v.x, -v.y)
- 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)
- return a.x*b.y - a.y*b.x
- 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)
- return Vec2(v.x/divisor, v.y/divisor)
- func length(v:Vec2->Num; inline)
- return (v.x*v.x + v.y*v.y).sqrt()
- func dist(a,b:Vec2->Num; inline)
- return a.minus(b).length()
- 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
- return v
- len := v.length()
- return Vec2(v.x/len, v.y/len)
- 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)
- return Vec2(
- amount.mix(a.x, b.x),
- amount.mix(a.y, b.y),
- )
-
-struct Vec3(x,y,z:Num)
- ZERO := Vec3(0, 0, 0)
- 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)
- return Vec3(a.x-b.x, a.y-b.y, a.z-b.z)
- 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)
- return Vec3(-v.x, -v.y, -v.z)
- 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)
- 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)
- return Vec3(v.x*k, v.y*k, v.z*k)
- 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)
- return (v.x*v.x + v.y*v.y + v.z*v.z).sqrt()
- 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
- return v
- len := v.length()
- return Vec3(v.x/len, v.y/len, v.z/len)
- func mix(a,b:Vec3, amount:Num -> Vec3)
- return Vec3(
- amount.mix(a.x, b.x),
- amount.mix(a.y, b.y),
- amount.mix(a.z, b.z),
- )
-
-
-struct IVec2(x,y:Int)
- ZERO := IVec2(0, 0)
- func plus(a,b:IVec2->IVec2; inline)
- return IVec2(a.x+b.x, a.y+b.y)
- func minus(a,b:IVec2->IVec2; inline)
- return IVec2(a.x-b.x, a.y-b.y)
- func times(a,b:IVec2->IVec2; inline)
- return IVec2(a.x*b.x, a.y*b.y)
- func negative(v:IVec2->IVec2; inline)
- return IVec2(-v.x, -v.y)
- 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)
- return a.x*b.y - a.y*b.x
- 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)
- return IVec2(v.x/divisor, v.y/divisor)
- 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)
- return a.minus(b).length()
- func angle(v:IVec2->Num; inline)
- return Num.atan2(Num(v.y), Num(v.x))
-
-struct IVec3(x,y,z:Int)
- ZERO := IVec3(0, 0, 0)
- 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)
- return IVec3(a.x-b.x, a.y-b.y, a.z-b.z)
- 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)
- return IVec3(-v.x, -v.y, -v.z)
- 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)
- 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)
- return IVec3(v.x*k, v.y*k, v.z*k)
- 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)
- 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)
- return a.minus(b).length()
-
-func main()
- >> Vec2(10, 20)
- >> Vec2(10, 20) + Vec2(100, 100)
- = Vec2(x=110, y=120)
- >> Vec3(10, 20, 30)
- >> Vec2.ZERO
-
diff --git a/examples/wrap/CHANGES.md b/examples/wrap/CHANGES.md
deleted file mode 100644
index 42ae752c..00000000
--- a/examples/wrap/CHANGES.md
+++ /dev/null
@@ -1,5 +0,0 @@
-# Version History
-
-## v1.0
-
-Initial version
diff --git a/examples/wrap/wrap.tm b/examples/wrap/wrap.tm
deleted file mode 100644
index bae01739..00000000
--- a/examples/wrap/wrap.tm
+++ /dev/null
@@ -1,102 +0,0 @@
-HELP := "
- wrap: A tool for wrapping lines of text
-
- usage: wrap [--help] [files...] [--width=80] [--inplace=no] [--min_split=3] [--no-rewrap] [--hyphen='-']
- --help: Print this message and exit
- [files...]: The files to wrap (stdin is used if no files are provided)
- --width=N: The width to wrap the text
- --inplace: Whether or not to perform the modification in-place or print the output
- --min-split=N: The minimum amount of text on either end of a hyphenation split
- --rewrap|--no-rewrap: Whether to rewrap text that is already wrapped or only split long lines
- --hyphen='-': The text to use for hyphenation
-"
-
-UNICODE_HYPHEN := "\{hyphen}"
-
-func unwrap(text:Text, preserve_paragraphs=yes, hyphen=UNICODE_HYPHEN -> Text)
- if preserve_paragraphs
- paragraphs := text.split($/{2+ nl}/)
- 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
- fail("Width must be a positive integer, not $width")
-
- if 2*min_split - hyphen.length > width
- fail("
- Minimum word split length ($min_split) is too small for the given wrap width ($width)!
-
- I can't fit a $(2*min_split - hyphen.length)-wide word on a line without splitting it,
- ... and I can't split it without splitting into chunks smaller than $min_split.
- ")
-
- lines : @[Text]
- line := ""
- for word in text.split($/{whitespace}/)
- letters := word.split()
- skip if letters.length == 0
-
- while not _can_fit_word(line, letters, width)
- line_space := width - line.length
- if line != "" then line_space -= 1
-
- 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 != "" then line ++= " "
- line ++= ((++: letters.to(split)) or "") ++ hyphen
- letters = letters.from(split + 1)
- else if line == ""
- # Force split word without hyphenation:
- if line != "" then line ++= " "
- line ++= (++: letters.to(line_space)) or ""
- letters = letters.from(line_space + 1)
- else
- pass # Move to next line
-
- lines.insert(line)
- line = ""
-
- if letters.length > 0
- if line != "" then line ++= " "
- line ++= (++: letters) or ""
-
- if line != ""
- lines.insert(line)
-
- return "\n".join(lines)
-
-func _can_fit_word(line:Text, letters:[Text], width:Int -> Bool; inline)
- if line == ""
- return letters.length <= width
- 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
- files = [(/dev/stdin)]
-
- for file in files
- text := file.read() or exit("Could not read file: $file")
-
- if rewrap
- text = unwrap(text)
-
- out := if file.is_file() and inplace
- file
- else
- (/dev/stdout)
-
- first := yes
- wrapped_paragraphs : @[Text]
- for paragraph in text.split($/{2+ nl}/)
- wrapped_paragraphs.insert(
- wrap(paragraph, width=width, min_split=min_split, hyphen=hyphen)
- )
-
- out.write("\n\n".join(wrapped_paragraphs[]) ++ "\n")