Deprecate built-in Moment datatype in favor of a time module

This commit is contained in:
Bruce Hill 2025-03-30 17:27:52 -04:00
parent d853d7b8dc
commit 8cba6c3c24
26 changed files with 298 additions and 1269 deletions

View File

@ -106,7 +106,7 @@ clean:
examples: examples/base64/base64 examples/ini/ini examples/game/game \
examples/tomodeps/tomodeps examples/tomo-install/tomo-install examples/wrap/wrap examples/colorful/colorful
./build/tomo -qIL examples/commands examples/shell examples/base64 examples/log examples/ini examples/vectors examples/game \
./build/tomo -qIL examples/time examples/commands examples/shell examples/base64 examples/log examples/ini examples/vectors examples/game \
examples/http examples/threads examples/tomodeps examples/tomo-install examples/wrap examples/pthreads examples/colorful
./build/tomo examples/learnxiny.tm

View File

@ -21,7 +21,6 @@ Information about Tomo's built-in types can be found here:
- [Arrays](arrays.md)
- [Booleans](booleans.md)
- [Bytes](bytes.md)
- [Moment](moments.md)
- [Enums](enums.md)
- [Floating point numbers](nums.md)
- [Integers](integers.md)
@ -167,23 +166,3 @@ Nothing, aborts the program.
```tomo
fail("Oh no!")
```
---
### `now`
Gets the current time. This is an alias for `Moment.now()`.
```tomo
func now(->Moment)
```
None.
**Returns:**
The current moment as a Moment.
**Example:**
```tomo
>> now()
= Sun Sep 29 20:12:33 2024
```

View File

@ -1,642 +0,0 @@
# Moments
Tomo has a builtin datatype for representing a specific single point in time:
`Moment`. This is similar to a "datetime" in other languages, but it does not
represent a locale-specific time with a timezone. A Moment object is internally
represented using a UNIX timestamp in seconds and a number of nanoseconds to
represent sub-second times (in C, the equivalent of `struct timeval`). Moment
values do not represent calendar dates or clock times, they represent an exact
nanosecond-long sliver of time, such as the moment when a file was last
modified on the filesystem or the current moment (`Moment.now()`).
⚠️⚠️⚠️ **WARNING** ⚠️⚠️⚠️ Dates and times are deeply counterintuitive and you should
be extremely cautious when writing code that deals with dates and times. Effort
has been made to ensure that Tomo's `Moment` code uses standard libraries and
is as correct as possible, but counterintuitive behaviors around time zones,
daylight savings time, leap seconds, and other anomalous time situations can
still cause bugs if you're not extremely careful.
## Syntax
Moment literals can be specified using [ISO
8601](https://en.wikipedia.org/wiki/ISO_8601) syntax with an optional
square-bracket delimited time zone name afterwards. A space may be used instead
of a `T` in the ISO 8601 format for readability, and spaces may come before the
timezone.
```tomo
2024-09-30
2024-09-30T13:57
2024-09-30 13:57
2024-09-30 13:57:01
2024-09-30 13:57:01 +04:00
2024-09-30 13:57:01 [America/New_York]
```
## Time Zones
Because humans are not able to easily understand UNIX timestamps, the default
textual representation of `Moment` objects uses the current locale's
preferred representation of the Moment in the current time zone:
```tomo
>> Moment.now()
= Sun Sep 29 18:20:12 2024 EDT
```
For various methods, it is assumed by default that users wish to perform
calculations and specify moments using the local time zone and daylight
savings time rules. For example, if a program is running in New York and it is
currently 11pm on February 28th, 2023 (the last day of the month) in local
time, it is assumed that "one month from now" refers to 11pm on March 28th,
2024 in local time, rather than referring to one month from the current UTC
time. In that example, the initial time would be 3am March 1, 2023 in UTC, so
one month later would be 3am April 1, 2023 in UTC, which is which is 11am March
31st in local time. Most users would be unpleasantly surprised to find out that
when it's February 28th in local time, one month later is March 28th until 8pm,
at which point it becomes March 31st!
For various functions where time zones matter, there is an optional `timezone`
argument that, if set, will override the timezone when performing calculations.
If unspecified, it is assumed that the current local timezone should be used.
Time zones are specified by name, such as `America/New_York` or `UTC`.
## Moment Methods
- [`func after(moment: Moment, seconds : Num = 0.0, minutes : Num = 0.0, hours : Num = 0.0, days : Int = 0, weeks : Int = 0, months : Int = 0, years : Int = 0, timezone : Text? = !Text -> Moment)`](#after)
- [`func date(moment: Moment, timezone : Text? = !Text -> Text)`](#date)
- [`func day_of_month(moment: Moment, timezone : Text? = !Text -> Int)`](#day_of_month)
- [`func day_of_week(moment: Moment, timezone : Text? = !Text -> Int)`](#day_of_week)
- [`func day_of_year(moment: Moment, timezone : Text? = !Text -> Int)`](#day_of_year)
- [`func format(moment: Moment, format: Text = "%Y-%m-%dT%H:%M:%S%z", timezone : Text? = !Text -> Text)`](#format)
- [`func from_unix_timestamp(timestamp: Int64 -> Moment)`](#from_unix_timestamp)
- [`func get_local_timezone(->Text)`](#get_local_timezone)
- [`func hour(moment: Moment, timezone : Text? = !Text -> Int)`](#hour)
- [`func hours_till(moment: Moment, then:Moment -> Num)`](#hours_till)
- [`func minute(moment: Moment, timezone : Text? = !Text -> Int)`](#minute)
- [`func minutes_till(moment: Moment, then:Moment -> Num)`](#minutes_till)
- [`func month(moment: Moment, timezone : Text? = !Text -> Int)`](#month)
- [`func nanosecond(moment: Moment, timezone : Text? = !Text -> Int)`](#nanosecond)
- [`func new(year : Int, month : Int, day : Int, hour : Int = 0, minute : Int = 0, second : Num = 0.0 -> Moment)`](#new)
- [`func now(->Moment)`](#now)
- [`func parse(text: Text, format: Text = "%Y-%m-%dT%H:%M:%S%z" -> Moment?)`](#parse)
- [`func relative(moment: Moment, relative_to : Moment = Moment.now(), timezone : Text? = !Text -> Text)`](#relative)
- [`func second(moment: Moment, timezone : Text? = !Text -> Int)`](#second)
- [`func seconds_till(moment: Moment, then:Moment -> Num)`](#seconds_till)
- [`func set_local_timezone(timezone : Text? = !Text -> Void)`](#set_local_timezone)
- [`func time(moment: Moment, seconds : Bool = no, am_pm : Bool = yes, timezone : Text? = !Text -> Text)`](#time)
- [`func unix_timestamp(moment:Moment->Int64)`](#unix_timestamp)
### `after`
Returns a Moment that occurs after the specified time differences. Time
differences may be either positive or negative.
**Note:** time offsets for days, months, weeks, and years do not refer to fixed
time intervals, but are relative to which date they are applied to. For
example, one year from January 1, 2024 is January 1, 2025, which is 366 days
later because 2024 is a leap year. Similarly, adding one month may add anywhere
from 28 to 31 days, depending on the starting month. Days and weeks are
affected by leap seconds. For this reason, `after()` takes an argument,
`timezone` which is used to determine in which timezone the offsets should be
calculated.
```tomo
func after(moment: Moment, seconds : Num = 0.0, minutes : Num = 0.0, hours : Num = 0.0, days : Int = 0, weeks : Int = 0, months : Int = 0, years : Int = 0, timezone : Text? = !Text -> Moment)
```
- `moment`: The moment used as a starting point.
- `seconds` (optional): An amount of seconds to offset the moment (default: 0).
- `minutes` (optional): An amount of minutes to offset the moment (default: 0).
- `hours` (optional): An amount of hours to offset the moment (default: 0).
- `days` (optional): An amount of days to offset the moment (default: 0).
- `weeks` (optional): An amount of weeks to offset the moment (default: 0).
- `months` (optional): An amount of months to offset the moment (default: 0).
- `years` (optional): An amount of years to offset the moment (default: 0).
- `timezone` (optional): If specified, perform perform the calculations in the
given timezone. If unspecified, the current local timezone will be used.
**Returns:**
A new `Moment` offset by the given amount.
**Example:**
```tomo
>> Moment(2024, 9, 29, hour=19):after(days=1, minutes=30)
= Mon Sep 30 19:30:00 2024 EDT
```
---
### `date`
Return a text representation of the moment using the `"%F"` format
specifier, which gives the date in `YYYY-MM-DD` form.
```tomo
func date(moment: Moment, timezone : Text? = !Text -> Text)
```
- `moment`: The moment to get the date from.
- `timezone` (optional): If specified, give the date in the given timezone (otherwise, use the current local timezone).
**Returns:**
The date in `YYYY-MM-DD` format.
**Example:**
```tomo
>> Moment(2024, 9, 29):date()
= "2024-09-29"
```
---
### `day_of_month`
Return the integer day of the month (1-31).
```tomo
func day_of_month(moment: Moment, timezone : Text? = !Text -> Int)
```
- `moment`: The moment to get the day of the month from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
The day of the month as an integer (1-31).
**Example:**
```tomo
>> Moment(2024, 9, 29):day_of_month()
= 29
```
---
### `day_of_week`
Return the integer day of the week (1-7), where 1 = Sunday, 2 = Monday,
3 = Tuesday, 4 = Wednesday, 5 = Thursday, 6 = Friday, 7 = Saturday.
```tomo
func day_of_week(moment: Moment, timezone : Text? = !Text -> Int)
```
- `moment`: The moment to get the day of the week from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
The day of the week as an integer (1-7).
**Example:**
```tomo
>> Moment(2024, 9, 29):day_of_week()
= 1
```
---
### `day_of_year`
Return the integer day of the year (1-366, including leap years).
```tomo
func day_of_year(moment: Moment, timezone : Text? = !Text -> Int)
```
- `moment`: The moment to get the day of the year from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
The day of the year as an integer (1-366).
**Example:**
```tomo
>> Moment(2024, 9, 29):day_of_year()
= 272
```
---
### `format`
Using the C-style [`strftime`](https://linux.die.net/man/3/strftime) format
options, return a text representation of the given date in the given format. If
`timezone` is specified, use that timezone instead of the current local
timezone.
```tomo
func format(moment: Moment, format: Text = "%Y-%m-%dT%H:%M:%S%z", timezone : Text? = !Text -> Text)
```
- `moment`: The moment to format.
- `format`: The `strftime` format to use (default: `"%Y-%m-%dT%H:%M:%S%z"`).
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
Nothing.
**Example:**
```tomo
>> Moment(2024, 9, 29):format("%A")
= "Sunday"
```
---
### `from_unix_timestamp`
Return a moment object that represents the same moment in time as
the given UNIX epoch timestamp (seconds since January 1, 1970 UTC).
```tomo
func from_unix_timestamp(timestamp: Int64 -> Moment)
```
- `timestamp`: The UNIX timestamp.
**Returns:**
A `Moment` object representing the same moment as the given UNIX timestamp.
**Example:**
```tomo
# In the New York timezone:
>> Moment.from_unix_timestamp(0)
= Wed Dec 31 19:00:00 1969
```
### `get_local_timezone`
Get the local timezone's name (e.g. `America/New_York` or `UTC`. By default,
this value is read from `/etc/localtime`, however, this can be overridden by
calling `Moment.set_local_timezone(...)`.
```tomo
func get_local_timezone(->Text)
```
None.
**Returns:**
The name of the current local timezone.
**Example:**
```tomo
>> Moment.get_local_timezone()
= "America/New_York"
```
---
### `hour`
Return the hour of the day as an integer (1-24).
```tomo
func hour(moment: Moment, timezone : Text? = !Text -> Int)
```
- `moment`: The moment to get the hour from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
The hour of the day as an integer (1-24).
**Example:**
```tomo
>> Moment(2024, 9, 29, 11, 59):hour()
= 11
```
---
### `hours_till`
Return the number of hours until a given moment.
```tomo
func hours_till(moment: Moment, then:Moment -> Num)
```
- `moment`: The starting point moment.
- `then`: Another moment that we want to calculate the time offset from (in hours).
**Returns:**
The number of hours (possibly fractional, possibly negative) until the given time.
**Example:**
```tomo
the_future := now():after(hours=1, minutes=30)
>> now():hours_till(the_future)
= 1.5
```
---
### `minute`
Return the minute of the day as an integer (0-59).
```tomo
func minute(moment: Moment, timezone : Text? = !Text -> Int)
```
- `moment`: The moment to get the minute from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
The minute of the hour as an integer (0-59).
**Example:**
```tomo
>> Moment(2024, 9, 29, 11, 59):minute()
= 59
```
---
### `minutes_till`
Return the number of minutes until a given moment.
```tomo
func minutes_till(moment: Moment, then:Moment -> Num)
```
- `moment`: The starting point moment.
- `then`: Another moment that we want to calculate the time offset from (in minutes).
**Returns:**
The number of minutes (possibly fractional, possibly negative) until the given time.
**Example:**
```tomo
the_future := now():after(minutes=1, seconds=30)
>> now():minutes_till(the_future)
= 1.5
```
---
### `month`
Return the month of the year as an integer (1-12).
```tomo
func month(moment: Moment, timezone : Text? = !Text -> Int)
```
- `moment`: The moment to get the month from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
The month of the year as an integer (1-12).
**Example:**
```tomo
>> Moment(2024, 9, 29, 11, 59):month()
= 9
```
---
### `nanosecond`
Return the nanosecond of the second as an integer (0-999,999,999).
```tomo
func nanosecond(moment: Moment, timezone : Text? = !Text -> Int)
```
- `moment`: The moment to get the nanosecond from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
The nanosecond of the second as an integer (0-999,999,999).
**Example:**
```tomo
>> Moment(2024, 9, 29, 11, 59):month()
= 9
```
---
### `new`
Return a new `Moment` object representing the given time parameters expressed
in local time. This function is the same as calling `Moment` directly as a
constructor.
```tomo
func new(year : Int, month : Int, day : Int, hour : Int = 0, minute : Int = 0, second : Num = 0.0 -> Moment)
```
- `year`: The year.
- `month`: The month of the year (1-12).
- `day`: The day of the month (1-31).
- `hour`: The hour of the day (0-23) (default: 0).
- `minute`: The minute of the hour (0-59) (default: 0).
- `second`: The second of the minute (0-59) (default: 0.0).
**Returns:**
A `Moment` representing the given information in local time. If the given
parameters exceed reasonable bounds, the time values will wrap around. For
example, `Moment.new(..., hour=3, minute=65)` is the same as
`Moment.new(..., hour=4, minute=5)`. If any arguments cannot fit in a 32-bit
integer, an error will be raised.
**Example:**
```tomo
>> Moment.new(2024, 9, 29)
= Mon Sep 30 00:00:00 2024 EDT
# March 1642, 2020:
>> Moment(2020, 4, 1643)
= Sat Sep 28 00:00:00 2024 EDT
```
---
### `now`
Get a `Moment` object representing the current date and time. This function
is the same as the global function `now()`.
```tomo
func now(->Moment)
```
None.
**Returns:**
Returns a `Moment` object representing the current date and time.
**Example:**
```tomo
>> Moment.now()
= Sun Sep 29 20:22:48 2024 EDT
```
---
### `parse`
Return a new `Moment` object parsed from the given string in the given format,
or `none` if the value could not be successfully parsed.
```tomo
func parse(text: Text, format: Text = "%Y-%m-%dT%H:%M:%S%z" -> Moment?)
```
- `text`: The text to parse.
- `format`: The date format of the text being parsed (see:
[strptime](https://linux.die.net/man/3/strptime) for more info on this
format) (default: `"%Y-%m-%dT%H:%M:%S%z"`).
**Returns:**
If the text was successfully parsed according to the given format, return a
`Moment` representing that information. Otherwise, return `none`.
**Example:**
```tomo
>> Moment.parse("2024-09-29", "%Y-%m-%d")!
= Sun Sep 29 00:00:00 2024 EDT
>> Moment.parse("???", "%Y-%m-%d")
= !Moment
```
---
### `relative`
Return a plain English textual representation of the approximate time difference
between two `Moment`s. For example: `5 minutes ago` or `1 day later`
```tomo
func relative(moment: Moment, relative_to : Moment = Moment.now(), timezone : Text? = !Text -> Text)
```
- `moment`: The moment whose relative time you're getting.
- `relative_to` (optional): The time against which the relative time is calculated (default: `Moment.now()`).
- `timezone` (optional): If specified, perform calculations in the given
timezone (otherwise, use the current local timezone).
**Returns:**
Return a plain English textual representation of the approximate time
difference between two `Moment`s. For example: `5 minutes ago` or `1 day
later`. Return values are approximate and use only one significant unit of
measure with one significant digit, so a difference of 1.6 days will be
represented as `2 days later`. moments in the past will have the suffix `"
ago"`, while moments in the future will have the suffix `" later"`.
**Example:**
```tomo
>> now():after(days=2):relative()
= "2 days later"
>> now():after(minutes=-65):relative()
= "1 hour ago"
```
---
### `second`
Return the second of the minute as an integer (0-59).
```tomo
func second(moment: Moment, timezone : Text? = !Text -> Int)
```
- `moment`: The moment to get the second from.
- `timezone` (optional): If specified, use the given timezone (otherwise, use the current local timezone).
**Returns:**
The second of the hour as an integer (0-59).
**Example:**
```tomo
>> Moment(2024, 9, 29, 11, 30, 59):second()
= 59
```
---
### `seconds_till`
Return the number of seconds until a given moment.
```tomo
func seconds_till(moment: Moment, then:Moment -> Num)
```
- `moment`: The starting point moment.
- `then`: Another moment that we want to calculate the time offset from (in seconds).
**Returns:**
The number of seconds (possibly fractional, possibly negative) until the given time.
**Example:**
```tomo
the_future := now():after(seconds=1)
>> now():seconds_till(the_future)
= 1
```
---
### `set_local_timezone`
Set the current local timezone to a given value by name (e.g.
`America/New_York` or `UTC`). The local timezone is used as the default
timezone for performing calculations and constructing `Moment` objects from
component parts. It's also used as the default way that `Moment` objects are
converted to text.
```tomo
func set_local_timezone(timezone : Text? = !Text -> Void)
```
- `timezone` (optional): if specified, set the current local timezone to the
timezone with the given name. If null, reset the current local timezone to
the system default (the value referenced in `/etc/localtime`).
**Returns:**
Nothing.
**Example:**
```tomo
Moment.set_local_timezone("America/Los_Angeles")
```
---
### `time`
Return a text representation of the time component of the given moment.
```tomo
func time(moment: Moment, seconds : Bool = no, am_pm : Bool = yes, timezone : Text? = !Text -> Text)
```
- `moment`: The moment whose time value you want to get.
- `seconds`: Whether to include seconds in the time (default: `no`).
- `am_pm`: Whether to use am/pm in the representation or use a 24-hour clock (default: `yes`).
- `timezone` (optional): If specified, give the time in the given timezone (otherwise, use the current local timezone).
**Returns:**
A text representation of the time component of the moment.
**Example:**
```tomo
moment := Moment(2024, 9, 29, hours=13, minutes=59, seconds=30)
>> dt:time()
= "1:59pm"
>> dt:time(am_pm=no)
= "13:59"
>> dt:time(seconds=yes)
= "1:59:30pm"
```
---
### `unix_timestamp`
Get the UNIX timestamp of the given moment (seconds since the UNIX epoch:
January 1, 1970 UTC).
```tomo
func unix_timestamp(moment:Moment->Int64)
```
`moment`: The moment whose UNIX timestamp you want to get.
**Returns:**
A 64-bit integer representation of the UNIX timestamp.
**Example:**
```tomo
>> now():unix_timestamp()
= 1727654730[64]
```

View File

@ -37,7 +37,7 @@ intended. Paths can be created from text with slashes using
## Path Methods
- [`func accessed(path:Path, follow_symlinks=yes -> Moment?)`](#accessed)
- [`func accessed(path:Path, follow_symlinks=yes -> Int64?)`](#accessed)
- [`func append(path: Path, text: Text, permissions: Int32 = 0o644[32] -> Void)`](#append)
- [`func append_bytes(path: Path, bytes: [Byte], permissions: Int32 = 0o644[32] -> Void)`](#append_bytes)
- [`func base_name(path: Path -> Text)`](#base_name)
@ -45,7 +45,7 @@ intended. Paths can be created from text with slashes using
- [`func can_execute(path:Path -> Bool)`](#can_execute)
- [`func can_read(path:Path -> Bool)`](#can_read)
- [`func can_write(path:Path -> Bool)`](#can_write)
- [`func changed(path:Path, follow_symlinks=yes -> Moment?)`](#changed)
- [`func changed(path:Path, follow_symlinks=yes -> Int64?)`](#changed)
- [`func child(path: Path, child:Text -> Path)`](#child)
- [`func children(path: Path, include_hidden=no -> [Path])`](#children)
- [`func create_directory(path: Path, permissions=0o755[32] -> Void)`](#create_directory)
@ -60,7 +60,7 @@ intended. Paths can be created from text with slashes using
- [`func is_file(path: Path, follow_symlinks=yes -> Bool)`](#is_file)
- [`func is_socket(path: Path, follow_symlinks=yes -> Bool)`](#is_socket)
- [`func is_symlink(path: Path -> Bool)`](#is_symlink)
- [`func modified(path:Path, follow_symlinks=yes -> Moment?)`](#modified)
- [`func modified(path:Path, follow_symlinks=yes -> Int64?)`](#modified)
- [`func owner(path: Path, follow_symlinks=yes -> Text?)`](#owner)
- [`func parent(path: Path -> Path)`](#parent)
- [`func read(path: Path -> Text?)`](#read)
@ -80,22 +80,22 @@ intended. Paths can be created from text with slashes using
Gets the file access time of a file.
```tomo
func accessed(path:Path, follow_symlinks: Bool = yes -> Moment?)
func accessed(path:Path, follow_symlinks: Bool = yes -> Int64?)
```
- `path`: The path of the file whose access time you want.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
A [Moment](moments.md) representing when the file or directory was last
A 64-bit unix epoch timestamp representing when the file or directory was last
accessed, or `none` if no such file or directory exists.
**Example:**
```tomo
>> (./file.txt):accessed()
= Sun 16 Mar 2025 03:43:53 PM EDT EDT : Moment?
= 1704221100?
>> (./not-a-file):accessed()
= none : Moment?
= none:Int64?
```
---
@ -274,22 +274,22 @@ Gets the file change time of a file.
which is _not_ the file creation time.
```tomo
func changed(path:Path, follow_symlinks: Bool = yes -> Moment?)
func changed(path:Path, follow_symlinks: Bool = yes -> Int64?)
```
- `path`: The path of the file whose change time you want.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
A [Moment](moments.md) representing when the file or directory was last
A 64-bit unix epoch timestamp representing when the file or directory was last
changed, or `none` if no such file or directory exists.
**Example:**
```tomo
>> (./file.txt):changed()
= Sun 16 Mar 2025 03:43:53 PM EDT EDT : Moment?
= 1704221100?
>> (./not-a-file):changed()
= none : Moment?
= none:Int64
```
---
@ -633,22 +633,22 @@ func is_symlink(path: Path -> Bool)
Gets the file modification time of a file.
```tomo
func modified(path:Path, follow_symlinks: Bool = yes -> Moment?)
func modified(path:Path, follow_symlinks: Bool = yes -> Int64?)
```
- `path`: The path of the file whose modification time you want.
- `follow_symlinks`: Whether to follow symbolic links (default is `yes`).
**Returns:**
A [Moment](moments.md) representing when the file or directory was last
A 64-bit unix epoch timestamp representing when the file or directory was last
modified, or `none` if no such file or directory exists.
**Example:**
```tomo
>> (./file.txt):modified()
= Sun 16 Mar 2025 03:43:53 PM EDT EDT : Moment?
= 1704221100?
>> (./not-a-file):modified()
= none : Moment?
= none:Int64
```
---
@ -714,10 +714,10 @@ raised.
**Example:**
```tomo
>> (./hello.txt):read()
= "Hello" : Text?
= "Hello"?
>> (./nosuchfile.xxx):read()
= none : Text?
= none:Text
```
---
@ -738,10 +738,10 @@ returned.
**Example:**
```tomo
>> (./hello.txt):read()
= [72[B], 101[B], 108[B], 108[B], 111[B]] : [Byte]?
= [72[B], 101[B], 108[B], 108[B], 111[B]]?
>> (./nosuchfile.xxx):read()
= none : [Byte]?
= none:[Byte]
```
---

210
examples/time/time.tm Normal file
View File

@ -0,0 +1,210 @@
# Time - a module for dealing with dates and times
use ./time_defs.h
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):
return inline C : Time {
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
fail("Couldn't get the time!");
(Time){.tv_sec=ts.tv_sec, .tv_usec=ts.tv_nsec/1000};
}
func local_timezone(->Text):
inline C {
if (_local_timezone.length < 0) {
static char buf[PATH_MAX];
ssize_t len = readlink("/etc/localtime", buf, sizeof(buf));
if (len < 0)
fail("Could not get local tz!");
char *zoneinfo = strstr(buf, "/zoneinfo/");
if (zoneinfo)
_local_timezone = Text$from_str(zoneinfo + strlen("/zoneinfo/"));
else
fail("Could not resolve local tz!");
}
}
return inline C : Text { _local_timezone; }
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):
return inline C : Text {
struct tm result;
time_t time = _$t.tv_sec;
struct tm *final_info;
WITH_TIMEZONE(_$timezone, final_info = localtime_r(&time, &result));
static char buf[256];
size_t len = strftime(buf, sizeof(buf), String(_$format), final_info);
Text$from_strn(buf, len);
}
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),
.tm_hour=Int32$from_int(_$hour, false),
.tm_mday=Int32$from_int(_$day, false),
.tm_mon=Int32$from_int(_$month, false) - 1,
.tm_year=Int32$from_int(_$year, false) - 1900,
.tm_isdst=-1,
};
time_t t;
WITH_TIMEZONE(_$timezone, t = mktime(&info));
(Time){.tv_sec=t + (time_t)_$second, .tv_usec=(suseconds_t)(fmod(_$second, 1.0) * 1e9)};
}
func unix_timestamp(t:Time -> Int64):
return inline C : Int64 { (int64_t)_$t.tv_sec }
func from_unix_timestamp(timestamp:Int64 -> Time):
return inline C : Time { (Time){.tv_sec=_$timestamp}; }
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):
return t:seconds_till(target)/60.
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):
inline C {
struct tm info = {};
struct tm relative_info = {};
WITH_TIMEZONE(_$timezone, {
localtime_r(&_$t.tv_sec, &info);
localtime_r(&_$relative_to.tv_sec, &relative_info);
});
double second_diff = _$time$Time$seconds_till(_$relative_to, _$t);
if (info.tm_year != relative_info.tm_year && fabs(second_diff) > 365.*24.*60.*60.)
return num_format((long)info.tm_year - (long)relative_info.tm_year, "year");
else if (info.tm_mon != relative_info.tm_mon && fabs(second_diff) > 31.*24.*60.*60.)
return num_format(12*((long)info.tm_year - (long)relative_info.tm_year) + (long)info.tm_mon - (long)relative_info.tm_mon, "month");
else if (info.tm_yday != relative_info.tm_yday && fabs(second_diff) > 24.*60.*60.)
return num_format(round(second_diff/(24.*60.*60.)), "day");
else if (info.tm_hour != relative_info.tm_hour && fabs(second_diff) > 60.*60.)
return num_format(round(second_diff/(60.*60.)), "hour");
else if (info.tm_min != relative_info.tm_min && fabs(second_diff) > 60.)
return num_format(round(second_diff/(60.)), "minute");
else {
if (fabs(second_diff) < 1e-6)
return num_format((long)(second_diff*1e9), "nanosecond");
else if (fabs(second_diff) < 1e-3)
return num_format((long)(second_diff*1e6), "microsecond");
else if (fabs(second_diff) < 1.0)
return num_format((long)(second_diff*1e3), "millisecond");
else
return num_format((long)(second_diff), "second");
}
}
fail("Unreachable")
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:
t:format("%T")
else if not seconds and am_pm:
t:format("%l:%M%P")
else:
t:format("%H:%M")
return time:trim($/ /, trim_left=yes, trim_right=yes)
func date(t:Time, timezone=Time.local_timezone() -> Text):
return t:format("%F")
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));
(_$time$TimeInfo$$type){
.year = I(info.tm_year + 1900),
.month = I(info.tm_mon + 1),
.day = I(info.tm_mday),
.hour = I(info.tm_hour),
.minute = I(info.tm_min),
.second = I(info.tm_sec),
.nanosecond = I(_$t.tv_usec),
.weekday = (_$time$Weekday$$type){.$tag=info.tm_wday + 1},
.day_of_year = I(info.tm_yday),
.timezone = _$timezone,
};
}
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;
struct tm info = {};
WITH_TIMEZONE(_$timezone, localtime_r(&_$t.tv_sec, &info));
info.tm_mday += Int32$from_int(_$days, false) + 7*Int32$from_int(_$weeks, false);
info.tm_mon += Int32$from_int(_$months, false);
info.tm_year += Int32$from_int(_$years, false);
time_t t = mktime(&info);
(Time){
.tv_sec=t,
.tv_usec=_$t.tv_usec + (suseconds_t)(fmod(offset, 1.0) * 1e9),
};
}
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);
const char *fmt = Text$as_c_string(_$format);
if (strstr(fmt, "%Z"))
fail("The %Z specifier is not supported for time parsing!");
char *invalid;
WITH_TIMEZONE(_$timezone, invalid = strptime(str, fmt, &info));
if (!invalid || invalid[0] != '\0')
return (_$time$$OptionalTime$$type){.is_none=true};
long offset = info.tm_gmtoff; // Need to cache this because mktime() mutates it to local tz >:(
time_t t;
WITH_TIMEZONE(_$timezone, t = mktime(&info));
(_$time$$OptionalTime$$type){.value={.tv_sec=t + offset - info.tm_gmtoff}};
}
func _run_tests():
>> Time.now():format()
>> Time.set_local_timezone("Europe/Paris")
>> Time.now():format()
>> Time.set_local_timezone("America/New_York")
>> Time.now():format()
# >> Time.now():format(timezone="Europe/Paris")
# >> Time.now():format()
# >> Time.now():format("%Y-%m-%d")
# >> Time.new(2023, 11, 5):format()
# >> Time.local_timezone()
# >> Time.new(2023, 11, 5):seconds_till(Time.now())
# >> Time.new(2023, 11, 5):relative()
# >> Time.now():info()
# >> Time.now():time()
# >> Time.now():date()
# >> 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():
_run_tests()

30
examples/time/time_defs.h Normal file
View File

@ -0,0 +1,30 @@
#pragma once
#include <time.h>
#include <sys/time.h>
typedef struct timeval Time;
static OptionalText_t _local_timezone = NONE_TEXT;
static INLINE Text_t num_format(long n, const char *unit)
{
if (n == 0)
return Text("now");
return Text$format((n == 1 || n == -1) ? "%ld %s %s" : "%ld %ss %s", n < 0 ? -n : n, unit, n < 0 ? "ago" : "later");
}
static void set_local_timezone(Text_t tz)
{
setenv("TZ", Text$as_c_string(tz), 1);
_local_timezone = tz;
tzset();
}
#define WITH_TIMEZONE(tz, body) ({ if (tz.length >= 0) { \
OptionalText_t old_timezone = _local_timezone; \
set_local_timezone(tz); \
body; \
set_local_timezone(old_timezone); \
} else { \
body; \
}})

View File

@ -163,7 +163,6 @@ CORD ast_to_xml(ast_t *ast)
T(FieldAccess, "<FieldAccess field=\"%s\">%r</FieldAccess>", data.field, ast_to_xml(data.fielded))
T(Optional, "<Optional>%r</Optional>", ast_to_xml(data.value))
T(NonOptional, "<NonOptional>%r</NonOptional>", ast_to_xml(data.value))
T(Moment, "<Moment/>")
T(DocTest, "<DocTest>%r%r</DocTest>", optional_tagged("expression", data.expr), optional_tagged("expected", data.expected))
T(Use, "<Use>%r%r</Use>", optional_tagged("var", data.var), xml_escape(data.path))
T(InlineCCode, "<InlineCode>%r</InlineCode>", xml_escape(data.code))

View File

@ -140,7 +140,6 @@ typedef enum {
Extern,
StructDef, EnumDef, LangDef,
Index, FieldAccess, Optional, NonOptional,
Moment,
DocTest,
Use,
InlineCCode,
@ -318,9 +317,6 @@ struct ast_s {
struct {
ast_t *value;
} Optional, NonOptional;
struct {
Moment_t moment;
} Moment;
struct {
ast_t *expr, *expected;
bool skip_source:1;

View File

@ -39,18 +39,18 @@ static CORD compile_string_literal(CORD literal);
CORD promote_to_optional(type_t *t, CORD code)
{
if (t == THREAD_TYPE || t == PATH_TYPE || t == PATH_TYPE_TYPE || t == MATCH_TYPE || t->tag == MomentType) {
if (t == THREAD_TYPE || t == PATH_TYPE || t == PATH_TYPE_TYPE || t == MATCH_TYPE) {
return code;
} else if (t->tag == IntType) {
switch (Match(t, IntType)->bits) {
case TYPE_IBITS8: return CORD_all("((OptionalInt8_t){", code, "})");
case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){", code, "})");
case TYPE_IBITS32: return CORD_all("((OptionalInt32_t){", code, "})");
case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){", code, "})");
case TYPE_IBITS8: return CORD_all("((OptionalInt8_t){.value=", code, "})");
case TYPE_IBITS16: return CORD_all("((OptionalInt16_t){.value=", code, "})");
case TYPE_IBITS32: return CORD_all("((OptionalInt32_t){.value=", code, "})");
case TYPE_IBITS64: return CORD_all("((OptionalInt64_t){.value=", code, "})");
default: errx(1, "Unsupported in type: ", type_to_str(t));
}
} else if (t->tag == ByteType) {
return CORD_all("((OptionalByte_t){", code, "})");
return CORD_all("((OptionalByte_t){.value=", code, "})");
} else if (t->tag == StructType) {
return CORD_all("({ ", compile_type(Type(OptionalType, .type=t)), " nonnull = {.value=", code, "}; nonnull.is_none = false; nonnull; })");
} else {
@ -516,7 +516,6 @@ CORD compile_type(type_t *t)
case BoolType: return "Bool_t";
case ByteType: return "Byte_t";
case CStringType: return "const char*";
case MomentType: return "Moment_t";
case BigIntType: return "Int_t";
case IntType: return CORD_asprintf("Int%ld_t", Match(t, IntType)->bits);
case NumType: return Match(t, NumType)->bits == TYPE_NBITS64 ? "Num_t" : CORD_asprintf("Num%ld_t", Match(t, NumType)->bits);
@ -563,7 +562,7 @@ CORD compile_type(type_t *t)
case TextType:
return Match(nonnull, TextType)->lang ? compile_type(nonnull) : "OptionalText_t";
case IntType: case BigIntType: case NumType: case BoolType: case ByteType:
case ArrayType: case TableType: case SetType: case MomentType:
case ArrayType: case TableType: case SetType:
return CORD_all("Optional", compile_type(nonnull));
case StructType: {
if (nonnull == THREAD_TYPE)
@ -688,7 +687,7 @@ CORD optional_into_nonnone(type_t *t, CORD value)
if (t->tag == OptionalType) t = Match(t, OptionalType)->type;
switch (t->tag) {
case IntType:
return CORD_all(value, ".i");
return CORD_all(value, ".value");
case StructType:
if (t == THREAD_TYPE || t == MATCH_TYPE || t == PATH_TYPE || t == PATH_TYPE_TYPE)
return value;
@ -730,8 +729,6 @@ CORD check_none(type_t *t, CORD value)
return CORD_all("(", value, ").is_none");
else if (t->tag == EnumType)
return CORD_all("({(", value, ").$tag == 0;})");
else if (t->tag == MomentType)
return CORD_all("({(", value, ").tv_usec < 0;})");
else if (t->tag == MutexedType)
return CORD_all("({", value, " == NULL;})");
print_err("Optional check not implemented for: ", type_to_str(t));
@ -1803,7 +1800,6 @@ CORD expr_as_text(CORD expr, type_t *t, CORD color)
// NOTE: this cannot use stack(), since bools may actually be bit fields:
return CORD_asprintf("Bool$as_text((Bool_t[1]){%r}, %r, &Bool$info)", expr, color);
case CStringType: return CORD_asprintf("CString$as_text(stack(%r), %r, &CString$info)", expr, color);
case MomentType: return CORD_asprintf("Moment$as_text(stack(%r), %r, &Moment$info)", expr, color);
case BigIntType: case IntType: case ByteType: case NumType: {
CORD name = type_to_cord(t);
return CORD_asprintf("%r$as_text(stack(%r), %r, &%r$info)", name, expr, color, name);
@ -2227,7 +2223,6 @@ CORD compile_none(type_t *t)
case SetType: return "NONE_TABLE";
case TextType: return "NONE_TEXT";
case CStringType: return "NULL";
case MomentType: return "NONE_MOMENT";
case PointerType: return CORD_all("((", compile_type(t), ")NULL)");
case ClosureType: return "NONE_CLOSURE";
case NumType: return "nan(\"null\")";
@ -2268,10 +2263,6 @@ CORD compile(env_t *env, ast_t *ast)
return compile_none(t);
}
case Bool: return Match(ast, Bool)->b ? "yes" : "no";
case Moment: {
auto moment = Match(ast, Moment)->moment;
return CORD_asprintf("((Moment_t){.tv_sec=%ld, .tv_usec=%ld})", moment.tv_sec, moment.tv_usec);
}
case Var: {
binding_t *b = get_binding(env, Match(ast, Var)->name);
if (b)
@ -3748,14 +3739,6 @@ CORD compile(env_t *env, ast_t *ast)
env_t *module_env = Table$str_get(*env->imports, name);
return compile(module_env, WrapAST(ast, Var, f->field));
}
case MomentType: {
if (streq(f->field, "seconds")) {
return CORD_all("I64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").tv_sec)");
} else if (streq(f->field, "microseconds")) {
return CORD_all("I64((", compile_to_pointer_depth(env, f->fielded, 0, false), ").tv_usec)");
}
code_err(ast, "There is no '", f->field, "' field on Moments");
}
default:
code_err(ast, "Field accesses are not supported on ", type_to_str(fielded_t), " values");
}
@ -3853,7 +3836,7 @@ CORD compile_type_info(type_t *t)
else if (t == PATH_TYPE_TYPE) return "&PathType$info";
switch (t->tag) {
case BoolType: case ByteType: case IntType: case BigIntType: case NumType: case CStringType: case MomentType:
case BoolType: case ByteType: case IntType: case BigIntType: case NumType: case CStringType:
return CORD_all("&", type_to_cord(t), "$info");
case TextType: {
auto text = Match(t, TextType);
@ -4134,8 +4117,8 @@ CORD compile_function(env_t *env, CORD name_code, ast_t *ast, CORD *staticdefs)
assert(args);
OptionalInt64_t cache_size = Int64$parse(Text$from_str(Match(cache, Int)->str));
CORD pop_code = CORD_EMPTY;
if (cache->tag == Int && !cache_size.is_none && cache_size.i > 0) {
pop_code = CORD_all("if (cache.entries.length > ", CORD_asprintf("%ld", cache_size.i),
if (cache->tag == Int && !cache_size.is_none && cache_size.value > 0) {
pop_code = CORD_all("if (cache.entries.length > ", CORD_asprintf("%ld", cache_size.value),
") Table$remove(&cache, cache.entries.data + cache.entries.stride*RNG$int64(default_rng, 0, cache.entries.length-1), table_type);\n");
}

View File

@ -288,41 +288,13 @@ env_t *global_env(void)
{"escape_int", "Int$value_as_text", "func(i:Int -> Pattern)"},
{"escape_text", "Pattern$escape_text", "func(text:Text -> Pattern)"},
)},
{"Moment", Type(MomentType), "Moment_t", "Moment", TypedArray(ns_entry_t,
// Used as a default for functions below:
{"now", "Moment$now", "func(->Moment)"},
{"after", "Moment$after", "func(moment:Moment,seconds,minutes,hours=0.0,days,weeks,months,years=0,timezone=none:Text -> Moment)"},
{"date", "Moment$date", "func(moment:Moment,timezone=none:Text -> Text)"},
{"day_of_month", "Moment$day_of_month", "func(moment:Moment,timezone=none:Text -> Int)"},
{"day_of_week", "Moment$day_of_week", "func(moment:Moment,timezone=none:Text -> Int)"},
{"day_of_year", "Moment$day_of_year", "func(moment:Moment,timezone=none:Text -> Int)"},
{"format", "Moment$format", "func(moment:Moment,format=\"%Y-%m-%dT%H:%M:%S%z\",timezone=none:Text -> Text)"},
{"from_unix_timestamp", "Moment$from_unix_timestamp", "func(timestamp:Int64 -> Moment)"},
{"get_local_timezone", "Moment$get_local_timezone", "func(->Text)"},
{"hour", "Moment$hour", "func(moment:Moment,timezone=none:Text -> Int)"},
{"hours_till", "Moment$hours_till", "func(now,then:Moment -> Num)"},
{"minute", "Moment$minute", "func(moment:Moment,timezone=none:Text -> Int)"},
{"minutes_till", "Moment$minutes_till", "func(now,then:Moment -> Num)"},
{"month", "Moment$month", "func(moment:Moment,timezone=none:Text -> Int)"},
{"microsecond", "Moment$microsecond", "func(moment:Moment,timezone=none:Text -> Int)"},
{"new", "Moment$new", "func(year,month,day:Int,hour,minute=0,second=0.0,timezone=none:Text -> Moment)"},
{"parse", "Moment$parse", "func(text:Text, format=\"%Y-%m-%dT%H:%M:%S%z\" -> Moment?)"},
{"relative", "Moment$relative", "func(moment:Moment,relative_to=Moment.now(),timezone=none:Text -> Text)"},
{"second", "Moment$second", "func(moment:Moment,timezone=none:Text -> Int)"},
{"seconds_till", "Moment$seconds_till", "func(now:Moment,then:Moment -> Num)"},
{"set_local_timezone", "Moment$set_local_timezone", "func(timezone=none:Text)"},
{"time", "Moment$time", "func(moment:Moment,seconds=no,am_pm=yes,timezone=none:Text -> Text)"},
{"unix_timestamp", "Moment$unix_timestamp", "func(moment:Moment -> Int64)"},
{"year", "Moment$year", "func(moment:Moment,timezone=none:Text -> Int)"},
)},
{"PathType", PATH_TYPE_TYPE, "PathType_t", "PathType$info", TypedArray(ns_entry_t,
{"Relative", "((PathType_t){.$tag=PATH_RELATIVE})", "PathType"},
{"Absolute", "((PathType_t){.$tag=PATH_ABSOLUTE})", "PathType"},
{"Home", "((PathType_t){.$tag=PATH_HOME})", "PathType"},
)},
{"Path", PATH_TYPE, "Path_t", "Path$info", TypedArray(ns_entry_t,
{"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Moment?)"},
{"accessed", "Path$accessed", "func(path:Path, follow_symlinks=yes -> Int64?)"},
{"append", "Path$append", "func(path:Path, text:Text, permissions=Int32(0o644))"},
{"append_bytes", "Path$append_bytes", "func(path:Path, bytes:[Byte], permissions=Int32(0o644))"},
{"base_name", "Path$base_name", "func(path:Path -> Text)"},
@ -330,7 +302,7 @@ env_t *global_env(void)
{"can_execute", "Path$can_execute", "func(path:Path -> Bool)"},
{"can_read", "Path$can_read", "func(path:Path -> Bool)"},
{"can_write", "Path$can_write", "func(path:Path -> Bool)"},
{"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> Moment?)"},
{"changed", "Path$changed", "func(path:Path, follow_symlinks=yes -> Int64?)"},
{"child", "Path$with_component", "func(path:Path, child:Text -> Path)"},
{"children", "Path$children", "func(path:Path, include_hidden=no -> [Path])"},
{"concatenated_with", "Path$concat", "func(a,b:Path -> Path)"},
@ -347,7 +319,7 @@ env_t *global_env(void)
{"is_pipe", "Path$is_pipe", "func(path:Path, follow_symlinks=yes -> Bool)"},
{"is_socket", "Path$is_socket", "func(path:Path, follow_symlinks=yes -> Bool)"},
{"is_symlink", "Path$is_symlink", "func(path:Path -> Bool)"},
{"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Moment?)"},
{"modified", "Path$modified", "func(path:Path, follow_symlinks=yes -> Int64?)"},
{"owner", "Path$owner", "func(path:Path, follow_symlinks=yes -> Text?)"},
{"parent", "Path$parent", "func(path:Path -> Path)"},
{"read", "Path$read", "func(path:Path -> Text?)"},
@ -562,10 +534,6 @@ env_t *global_env(void)
{"Path$escape_path", "func(path:Path -> Path)"},
{"Int$value_as_text", "func(i:Int -> Path)"});
ADD_CONSTRUCTORS("CString", {"Text$as_c_string", "func(text:Text -> CString)"});
ADD_CONSTRUCTORS("Moment",
{"Moment$now", "func(-> Moment)"},
{"Moment$new", "func(year,month,day:Int,hour,minute=0,second=0.0,timezone=none:Text -> Moment)"},
{"Moment$from_unix_timestamp", "func(timestamp:Int64 -> Moment)"});
ADD_CONSTRUCTORS("RNG", {"RNG$new", "func(-> RNG)"});
ADD_CONSTRUCTORS("Thread", {"Thread$new", "func(fn:func() -> Thread)"});
#undef ADD_CONSTRUCTORS
@ -591,7 +559,6 @@ env_t *global_env(void)
{"exit", "tomo_exit", "func(message=none:Text, code=Int32(1) -> Abort)"},
{"fail", "fail_text", "func(message:Text -> Abort)"},
{"sleep", "sleep_num", "func(seconds:Num)"},
{"now", "Moment$now", "func(->Moment)"},
};
for (size_t i = 0; i < sizeof(global_vars)/sizeof(global_vars[0]); i++) {
@ -756,7 +723,7 @@ env_t *get_namespace_by_type(env_t *env, type_t *t)
switch (t->tag) {
case ArrayType: return NULL;
case TableType: return NULL;
case CStringType: case MomentType:
case CStringType:
case BoolType: case IntType: case BigIntType: case NumType: case ByteType: {
binding_t *b = get_binding(env, CORD_to_const_char_star(type_to_cord(t)));
assert(b);

View File

@ -120,7 +120,6 @@ static PARSER(parse_holding);
static PARSER(parse_if);
static PARSER(parse_inline_c);
static PARSER(parse_int);
static PARSER(parse_moment);
static PARSER(parse_lambda);
static PARSER(parse_lang_def);
static PARSER(parse_mutexed);
@ -498,59 +497,6 @@ PARSER(parse_int) {
return NewAST(ctx->file, start, pos, Int, .str=str);
}
PARSER(parse_moment) {
const char *start = pos;
bool negative = match(&pos, "-");
if (!isdigit(*pos)) return NULL;
struct tm info = {.tm_isdst=-1};
char *after = strptime(pos, "%Y-%m-%d", &info);
if (!after) return NULL;
if (negative) info.tm_year = -(info.tm_year + 1900) - 1900;
pos = after;
if (match(&pos, "T") || spaces(&pos) >= 1) {
after = strptime(pos, "%H:%M", &info);
if (after) {
pos = after;
after = strptime(pos, ":%S", &info);
if (after) pos = after;
// TODO parse nanoseconds
}
}
const char *before_spaces = pos;
spaces(&pos);
Moment_t moment;
if (match(&pos, "[")) {
size_t tz_len = strcspn(pos, "\r\n]");
const char *tz = heap_strf("%.*s", tz_len, pos);
// TODO: check that tz is a valid timezone
pos += tz_len;
expect_closing(ctx, &pos, "]", "I wasn't able to parse the rest of this moment timezone");
const char *old_tz = getenv("TZ");
setenv("TZ", tz, 1);
tzset();
moment = (Moment_t){.tv_sec=mktime(&info)};
if (old_tz) setenv("TZ", old_tz, 1);
else unsetenv("TZ");
} else if (*pos == 'Z' || *pos == '-' || *pos == '+') {
after = strptime(pos, "%z", &info);
if (after) {
pos = after;
long offset = info.tm_gmtoff; // Need to cache this because mktime() mutates it to local timezone >:(
time_t t = mktime(&info);
moment = (Moment_t){.tv_sec=t + offset - info.tm_gmtoff};
} else {
moment = (Moment_t){.tv_sec=mktime(&info)};
}
} else {
pos = before_spaces;
moment = (Moment_t){.tv_sec=mktime(&info)};
}
return NewAST(ctx->file, start, pos, Moment, .moment=moment);
}
type_ast_t *parse_table_type(parse_ctx_t *ctx, const char *pos) {
const char *start = pos;
if (!match(&pos, "{")) return NULL;
@ -1590,7 +1536,6 @@ PARSER(parse_term_no_suffix) {
ast_t *term = NULL;
(void)(
false
|| (term=parse_moment(ctx, pos)) // Must come before num/int
|| (term=parse_none(ctx, pos))
|| (term=parse_num(ctx, pos)) // Must come before int
|| (term=parse_int(ctx, pos))

View File

@ -109,9 +109,6 @@ typedef struct {
} Path_t;
#define OptionalPath_t Path_t
typedef struct timeval Moment_t;
#define OptionalMoment_t Moment_t
typedef struct RNGState_t* RNG_t;
typedef struct MutexedData_s {

View File

@ -597,24 +597,24 @@ public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t
{ \
Optional##KindOfInt##_t i = info->current; \
if (!i.is_none) { \
KindOfInt##_t next; bool overflow = __builtin_add_overflow(i.i, info->step, &next); \
if (overflow || (!info->last.is_none && (info->step >= 0 ? next > info->last.i : next < info->last.i))) \
KindOfInt##_t next; bool overflow = __builtin_add_overflow(i.value, info->step, &next); \
if (overflow || (!info->last.is_none && (info->step >= 0 ? next > info->last.value : next < info->last.value))) \
info->current = (Optional##KindOfInt##_t){.is_none=true}; \
else \
info->current = (Optional##KindOfInt##_t){.i=next}; \
info->current = (Optional##KindOfInt##_t){.value=next}; \
} \
return i; \
} \
public to_attr Closure_t KindOfInt ## $to(c_type first, c_type last, Optional ## KindOfInt ## _t step) { \
KindOfInt##Range_t *range = GC_MALLOC(sizeof(KindOfInt##Range_t)); \
range->current = (Optional##KindOfInt##_t){.i=first}; \
range->last = (Optional##KindOfInt##_t){.i=last}; \
range->step = step.is_none ? (last >= first ? 1 : -1) : step.i; \
range->current = (Optional##KindOfInt##_t){.value=first}; \
range->last = (Optional##KindOfInt##_t){.value=last}; \
range->step = step.is_none ? (last >= first ? 1 : -1) : step.value; \
return (Closure_t){.fn=_next_##KindOfInt, .userdata=range}; \
} \
public to_attr Closure_t KindOfInt ## $onward(c_type first, c_type step) { \
KindOfInt##Range_t *range = GC_MALLOC(sizeof(KindOfInt##Range_t)); \
range->current = (Optional##KindOfInt##_t){.i=first}; \
range->current = (Optional##KindOfInt##_t){.value=first}; \
range->last = (Optional##KindOfInt##_t){.is_none=true}; \
range->step = step; \
return (Closure_t){.fn=_next_##KindOfInt, .userdata=range}; \
@ -628,7 +628,7 @@ public void Int32$deserialize(FILE *in, void *outval, Array_t*, const TypeInfo_t
if (Int$compare_value(full_int, I(max_val)) > 0) { \
return (Optional ## KindOfInt ## _t){.is_none=true}; \
} \
return (Optional ## KindOfInt ## _t){.i=KindOfInt##$from_int(full_int, true)}; \
return (Optional ## KindOfInt ## _t){.value=KindOfInt##$from_int(full_int, true)}; \
} \
public CONSTFUNC c_type KindOfInt ## $gcd(c_type x, c_type y) { \
if (x == 0 || y == 0) return 0; \

View File

@ -20,7 +20,7 @@
#define DEFINE_INT_TYPE(c_type, type_name) \
typedef struct { \
c_type i; \
c_type value; \
bool is_none:1; \
} Optional ## type_name ## _t; \
Text_t type_name ## $as_text(const void *i, bool colorize, const TypeInfo_t *type); \

View File

@ -1,323 +0,0 @@
// Moment methods/type info
#include <ctype.h>
#include <gc.h>
#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <unistd.h>
#include "datatypes.h"
#include "math.h"
#include "moments.h"
#include "optionals.h"
#include "patterns.h"
#include "stdlib.h"
#include "text.h"
#include "util.h"
static OptionalText_t _local_timezone = NONE_TEXT;
#define WITH_TIMEZONE(tz, body) ({ if (tz.length >= 0) { \
OptionalText_t old_timezone = _local_timezone; \
Moment$set_local_timezone(tz); \
body; \
Moment$set_local_timezone(old_timezone); \
} else { \
body; \
}})
public Text_t Moment$as_text(const void *moment, bool colorize, const TypeInfo_t*)
{
if (!moment)
return Text("Moment");
struct tm info;
struct tm *final_info = localtime_r(&((Moment_t*)moment)->tv_sec, &info);
static char buf[256];
size_t len = strftime(buf, sizeof(buf), "%c %Z", final_info);
Text_t text = Text$format("%.*s", (int)len, buf);
if (colorize)
text = Text$concat(Text("\x1b[36m"), text, Text("\x1b[m"));
return text;
}
PUREFUNC public int32_t Moment$compare(const void *va, const void *vb, const TypeInfo_t*)
{
Moment_t *a = (Moment_t*)va, *b = (Moment_t*)vb;
if (a->tv_sec != b->tv_sec)
return (a->tv_sec > b->tv_sec) - (a->tv_sec < b->tv_sec);
return (a->tv_usec > b->tv_usec) - (a->tv_usec < b->tv_usec);
}
CONSTFUNC public bool Moment$is_none(const void *m, const TypeInfo_t*)
{
return ((Moment_t*)m)->tv_usec < 0;
}
public Moment_t Moment$now(void)
{
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts) != 0)
fail("Couldn't get the time!");
return (Moment_t){.tv_sec=ts.tv_sec, .tv_usec=ts.tv_nsec/1000};
}
public Moment_t Moment$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second, OptionalText_t tz)
{
struct tm info = {
.tm_min=Int32$from_int(minute, false),
.tm_hour=Int32$from_int(hour, false),
.tm_mday=Int32$from_int(day, false),
.tm_mon=Int32$from_int(month, false) - 1,
.tm_year=Int32$from_int(year, false) - 1900,
.tm_isdst=-1,
};
time_t t;
WITH_TIMEZONE(tz, t = mktime(&info));
return (Moment_t){.tv_sec=t + (time_t)second, .tv_usec=(suseconds_t)(fmod(second, 1.0) * 1e9)};
}
public Moment_t Moment$after(Moment_t moment, double seconds, double minutes, double hours, Int_t days, Int_t weeks, Int_t months, Int_t years, OptionalText_t tz)
{
double offset = seconds + 60.*minutes + 3600.*hours;
moment.tv_sec += (time_t)offset;
struct tm info = {};
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
info.tm_mday += Int32$from_int(days, false) + 7*Int32$from_int(weeks, false);
info.tm_mon += Int32$from_int(months, false);
info.tm_year += Int32$from_int(years, false);
time_t t = mktime(&info);
return (Moment_t){
.tv_sec=t,
.tv_usec=moment.tv_usec + (suseconds_t)(fmod(offset, 1.0) * 1e9),
};
}
CONSTFUNC public double Moment$seconds_till(Moment_t now, Moment_t then)
{
return (double)(then.tv_sec - now.tv_sec) + 1e-9*(double)(then.tv_usec - now.tv_usec);
}
CONSTFUNC public double Moment$minutes_till(Moment_t now, Moment_t then)
{
return Moment$seconds_till(now, then)/60.;
}
CONSTFUNC public double Moment$hours_till(Moment_t now, Moment_t then)
{
return Moment$seconds_till(now, then)/3600.;
}
public void Moment$get(
Moment_t moment, Int_t *year, Int_t *month, Int_t *day, Int_t *hour, Int_t *minute, Int_t *second,
Int_t *nanosecond, Int_t *weekday, OptionalText_t tz)
{
struct tm info = {};
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
if (year) *year = I(info.tm_year + 1900);
if (month) *month = I(info.tm_mon + 1);
if (day) *day = I(info.tm_mday);
if (hour) *hour = I(info.tm_hour);
if (minute) *minute = I(info.tm_min);
if (second) *second = I(info.tm_sec);
if (nanosecond) *nanosecond = I(moment.tv_usec);
if (weekday) *weekday = I(info.tm_wday + 1);
}
public Int_t Moment$year(Moment_t moment, OptionalText_t tz)
{
struct tm info = {};
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
return I(info.tm_year + 1900);
}
public Int_t Moment$month(Moment_t moment, OptionalText_t tz)
{
struct tm info = {};
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
return I(info.tm_mon + 1);
}
public Int_t Moment$day_of_week(Moment_t moment, OptionalText_t tz)
{
struct tm info = {};
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
return I(info.tm_wday + 1);
}
public Int_t Moment$day_of_month(Moment_t moment, OptionalText_t tz)
{
struct tm info = {};
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
return I(info.tm_mday);
}
public Int_t Moment$day_of_year(Moment_t moment, OptionalText_t tz)
{
struct tm info = {};
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
return I(info.tm_yday);
}
public Int_t Moment$hour(Moment_t moment, OptionalText_t tz)
{
struct tm info = {};
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
return I(info.tm_hour);
}
public Int_t Moment$minute(Moment_t moment, OptionalText_t tz)
{
struct tm info = {};
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
return I(info.tm_min);
}
public Int_t Moment$second(Moment_t moment, OptionalText_t tz)
{
struct tm info = {};
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
return I(info.tm_sec);
}
public Int_t Moment$microsecond(Moment_t moment, OptionalText_t tz)
{
(void)tz;
return I(moment.tv_usec);
}
public Text_t Moment$format(Moment_t moment, Text_t fmt, OptionalText_t tz)
{
struct tm info;
WITH_TIMEZONE(tz, localtime_r(&moment.tv_sec, &info));
static char buf[256];
size_t len = strftime(buf, sizeof(buf), Text$as_c_string(fmt), &info);
return Text$format("%.*s", (int)len, buf);
}
public Text_t Moment$date(Moment_t moment, OptionalText_t tz)
{
return Moment$format(moment, Text("%F"), tz);
}
public Text_t Moment$time(Moment_t moment, bool seconds, bool am_pm, OptionalText_t tz)
{
Text_t text;
if (seconds)
text = Moment$format(moment, am_pm ? Text("%l:%M:%S%P") : Text("%T"), tz);
else
text = Moment$format(moment, am_pm ? Text("%l:%M%P") : Text("%H:%M"), tz);
return Text$trim(text, Pattern(" "), true, true);
}
public OptionalMoment_t Moment$parse(Text_t text, Text_t format)
{
struct tm info = {.tm_isdst=-1};
const char *str = Text$as_c_string(text);
const char *fmt = Text$as_c_string(format);
if (strstr(fmt, "%Z"))
fail("The %Z specifier is not supported for time parsing!");
char *invalid = strptime(str, fmt, &info);
if (!invalid || invalid[0] != '\0')
return NONE_MOMENT;
long offset = info.tm_gmtoff; // Need to cache this because mktime() mutates it to local tz >:(
time_t t = mktime(&info);
return (Moment_t){.tv_sec=t + offset - info.tm_gmtoff};
}
static INLINE Text_t num_format(long n, const char *unit)
{
if (n == 0)
return Text("now");
return Text$format((n == 1 || n == -1) ? "%ld %s %s" : "%ld %ss %s", n < 0 ? -n : n, unit, n < 0 ? "ago" : "later");
}
public Text_t Moment$relative(Moment_t moment, Moment_t relative_to, OptionalText_t tz)
{
struct tm info = {};
struct tm relative_info = {};
WITH_TIMEZONE(tz, {
localtime_r(&moment.tv_sec, &info);
localtime_r(&relative_to.tv_sec, &relative_info);
});
double second_diff = Moment$seconds_till(relative_to, moment);
if (info.tm_year != relative_info.tm_year && fabs(second_diff) > 365.*24.*60.*60.)
return num_format((long)info.tm_year - (long)relative_info.tm_year, "year");
else if (info.tm_mon != relative_info.tm_mon && fabs(second_diff) > 31.*24.*60.*60.)
return num_format(12*((long)info.tm_year - (long)relative_info.tm_year) + (long)info.tm_mon - (long)relative_info.tm_mon, "month");
else if (info.tm_yday != relative_info.tm_yday && fabs(second_diff) > 24.*60.*60.)
return num_format(round(second_diff/(24.*60.*60.)), "day");
else if (info.tm_hour != relative_info.tm_hour && fabs(second_diff) > 60.*60.)
return num_format(round(second_diff/(60.*60.)), "hour");
else if (info.tm_min != relative_info.tm_min && fabs(second_diff) > 60.)
return num_format(round(second_diff/(60.)), "minute");
else {
if (fabs(second_diff) < 1e-6)
return num_format((long)(second_diff*1e9), "nanosecond");
else if (fabs(second_diff) < 1e-3)
return num_format((long)(second_diff*1e6), "microsecond");
else if (fabs(second_diff) < 1.0)
return num_format((long)(second_diff*1e3), "millisecond");
else
return num_format((long)(second_diff), "second");
}
}
CONSTFUNC public Int64_t Moment$unix_timestamp(Moment_t moment)
{
return (Int64_t)moment.tv_sec;
}
CONSTFUNC public Moment_t Moment$from_unix_timestamp(Int64_t timestamp)
{
return (Moment_t){.tv_sec=(time_t)timestamp};
}
public void Moment$set_local_timezone(OptionalText_t tz)
{
if (tz.length >= 0) {
setenv("TZ", Text$as_c_string(tz), 1);
} else {
unsetenv("TZ");
}
_local_timezone = tz;
tzset();
}
public Text_t Moment$get_local_timezone(void)
{
if (_local_timezone.length < 0) {
static char buf[PATH_MAX];
ssize_t len = readlink("/etc/localtime", buf, sizeof(buf));
if (len < 0)
fail("Could not get local tz!");
char *zoneinfo = strstr(buf, "/zoneinfo/");
if (zoneinfo)
_local_timezone = Text$from_str(zoneinfo + strlen("/zoneinfo/"));
else
fail("Could not resolve local tz!");
}
return _local_timezone;
}
public const TypeInfo_t Moment$info = {
.size=sizeof(Moment_t),
.align=__alignof__(Moment_t),
.metamethods={
.as_text=Moment$as_text,
.compare=Moment$compare,
.is_none=Moment$is_none,
},
};
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -1,44 +0,0 @@
#pragma once
// Moment objects
#include <stdint.h>
#include "datatypes.h"
#include "integers.h"
#include "optionals.h"
#include "types.h"
#include "util.h"
Text_t Moment$as_text(const void *moment, bool colorize, const TypeInfo_t *type);
PUREFUNC int32_t Moment$compare(const void *a, const void *b, const TypeInfo_t *type);
CONSTFUNC public bool Moment$is_none(const void *m, const TypeInfo_t*);
Moment_t Moment$now(void);
Moment_t Moment$new(Int_t year, Int_t month, Int_t day, Int_t hour, Int_t minute, double second, OptionalText_t timezone);
Moment_t Moment$after(Moment_t moment, double seconds, double minutes, double hours, Int_t days, Int_t weeks, Int_t months, Int_t years, OptionalText_t timezone);
CONSTFUNC double Moment$seconds_till(Moment_t now, Moment_t then);
CONSTFUNC double Moment$minutes_till(Moment_t now, Moment_t then);
CONSTFUNC double Moment$hours_till(Moment_t now, Moment_t then);
Int_t Moment$year(Moment_t moment, OptionalText_t timezone);
Int_t Moment$month(Moment_t moment, OptionalText_t timezone);
Int_t Moment$day_of_week(Moment_t moment, OptionalText_t timezone);
Int_t Moment$day_of_month(Moment_t moment, OptionalText_t timezone);
Int_t Moment$day_of_year(Moment_t moment, OptionalText_t timezone);
Int_t Moment$hour(Moment_t moment, OptionalText_t timezone);
Int_t Moment$minute(Moment_t moment, OptionalText_t timezone);
Int_t Moment$second(Moment_t moment, OptionalText_t timezone);
Int_t Moment$microsecond(Moment_t moment, OptionalText_t timezone);
Text_t Moment$format(Moment_t moment, Text_t fmt, OptionalText_t timezone);
Text_t Moment$date(Moment_t moment, OptionalText_t timezone);
Text_t Moment$time(Moment_t moment, bool seconds, bool am_pm, OptionalText_t timezone);
OptionalMoment_t Moment$parse(Text_t text, Text_t format);
Text_t Moment$relative(Moment_t moment, Moment_t relative_to, OptionalText_t timezone);
CONSTFUNC Int64_t Moment$unix_timestamp(Moment_t moment);
CONSTFUNC Moment_t Moment$from_unix_timestamp(Int64_t timestamp);
void Moment$set_local_timezone(OptionalText_t timezone);
Text_t Moment$get_local_timezone(void);
extern const TypeInfo_t Moment$info;
// vim: ts=4 sw=0 et cino=L2,l1,(0,W4,m1,\:0

View File

@ -7,7 +7,6 @@
#include "datatypes.h"
#include "integers.h"
#include "metamethods.h"
#include "moments.h"
#include "nums.h"
#include "patterns.h"
#include "text.h"

View File

@ -16,7 +16,6 @@
#define NONE_TABLE ((OptionalTable_t){.entries.length=-1})
#define NONE_CLOSURE ((OptionalClosure_t){.fn=NULL})
#define NONE_TEXT ((OptionalText_t){.length=-1})
#define NONE_MOMENT ((OptionalMoment_t){.tv_usec=-1})
#define NONE_PATH ((Path_t){.type=PATH_NONE})
PUREFUNC bool is_null(const void *obj, const TypeInfo_t *non_optional_type);

View File

@ -255,28 +255,28 @@ public bool Path$can_execute(Path_t path)
#endif
}
public OptionalMoment_t Path$modified(Path_t path, bool follow_symlinks)
public OptionalInt64_t Path$modified(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
if (status != 0) return NONE_MOMENT;
return (Moment_t){.tv_sec=sb.st_mtime};
if (status != 0) return NONE_INT64;
return (OptionalInt64_t){.value=(int64_t)sb.st_mtime};
}
public OptionalMoment_t Path$accessed(Path_t path, bool follow_symlinks)
public OptionalInt64_t Path$accessed(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
if (status != 0) return NONE_MOMENT;
return (Moment_t){.tv_sec=sb.st_atime};
if (status != 0) return NONE_INT64;
return (OptionalInt64_t){.value=(int64_t)sb.st_atime};
}
public OptionalMoment_t Path$changed(Path_t path, bool follow_symlinks)
public OptionalInt64_t Path$changed(Path_t path, bool follow_symlinks)
{
struct stat sb;
int status = path_stat(path, follow_symlinks, &sb);
if (status != 0) return NONE_MOMENT;
return (Moment_t){.tv_sec=sb.st_ctime};
if (status != 0) return NONE_INT64;
return (OptionalInt64_t){.value=(int64_t)sb.st_ctime};
}
static void _write(Path_t path, Array_t bytes, int mode, int permissions)

View File

@ -28,9 +28,9 @@ bool Path$is_symlink(Path_t path);
bool Path$can_read(Path_t path);
bool Path$can_write(Path_t path);
bool Path$can_execute(Path_t path);
OptionalMoment_t Path$modified(Path_t path, bool follow_symlinks);
OptionalMoment_t Path$accessed(Path_t path, bool follow_symlinks);
OptionalMoment_t Path$changed(Path_t path, bool follow_symlinks);
OptionalInt64_t Path$modified(Path_t path, bool follow_symlinks);
OptionalInt64_t Path$accessed(Path_t path, bool follow_symlinks);
OptionalInt64_t Path$changed(Path_t path, bool follow_symlinks);
void Path$write(Path_t path, Text_t text, int permissions);
void Path$write_bytes(Path_t path, Array_t bytes, int permissions);
void Path$append(Path_t path, Text_t text, int permissions);

View File

@ -17,7 +17,6 @@
#include "integers.h"
#include "memory.h"
#include "metamethods.h"
#include "moments.h"
#include "mutexeddata.h"
#include "nums.h"
#include "optionals.h"

View File

@ -852,7 +852,7 @@ type_t *get_type(env_t *env, ast_t *ast)
if (constructor)
return t;
else if (t->tag == StructType || t->tag == IntType || t->tag == BigIntType || t->tag == NumType
|| t->tag == ByteType || t->tag == TextType || t->tag == CStringType || t->tag == MomentType)
|| t->tag == ByteType || t->tag == TextType || t->tag == CStringType)
return t; // Constructor
code_err(call->fn, "This is not a type that has a constructor");
}
@ -1408,7 +1408,6 @@ type_t *get_type(env_t *env, ast_t *ast)
type_ast_t *type_ast = inline_code->type_ast;
return type_ast ? parse_type_ast(env, type_ast) : Type(VoidType);
}
case Moment: return Type(MomentType);
case Unknown: code_err(ast, "I can't figure out the type of: ", ast_to_str(ast));
case Deserialize: return parse_type_ast(env, Match(ast, Deserialize)->type);
}

View File

@ -29,7 +29,6 @@ CORD type_to_cord(type_t *t) {
case BoolType: return "Bool";
case ByteType: return "Byte";
case CStringType: return "CString";
case MomentType: return "Moment";
case TextType: return Match(t, TextType)->lang ? Match(t, TextType)->lang : "Text";
case BigIntType: return "Int";
case IntType: return CORD_asprintf("Int%d", Match(t, IntType)->bits);
@ -497,7 +496,6 @@ PUREFUNC size_t type_size(type_t *t)
case BoolType: return sizeof(bool);
case ByteType: return sizeof(uint8_t);
case CStringType: return sizeof(char*);
case MomentType: return sizeof(Moment_t);
case BigIntType: return sizeof(Int_t);
case IntType: {
switch (Match(t, IntType)->bits) {
@ -590,7 +588,6 @@ PUREFUNC size_t type_align(type_t *t)
case BoolType: return __alignof__(bool);
case ByteType: return __alignof__(uint8_t);
case CStringType: return __alignof__(char*);
case MomentType: return __alignof__(Moment_t);
case BigIntType: return __alignof__(Int_t);
case IntType: {
switch (Match(t, IntType)->bits) {
@ -704,11 +701,6 @@ type_t *get_field_type(type_t *t, const char *field_name)
if (streq(field_name, "length")) return INT_TYPE;
return NULL;
}
case MomentType: {
if (streq(field_name, "seconds")) return Type(IntType, .bits=TYPE_IBITS64);
else if (streq(field_name, "microseconds")) return Type(IntType, .bits=TYPE_IBITS64);
return NULL;
}
default: return NULL;
}
}

View File

@ -46,7 +46,6 @@ struct type_s {
IntType,
NumType,
CStringType,
MomentType,
TextType,
ArrayType,
SetType,
@ -76,7 +75,7 @@ struct type_s {
struct {
enum { TYPE_NBITS32=32, TYPE_NBITS64=64 } bits;
} NumType;
struct {} CStringType, MomentType;
struct {} CStringType;
struct {
const char *lang;
struct env_s *env;

View File

@ -1,49 +0,0 @@
func main():
>> 2024-1-1 12:00[America/New_York] == 2024-1-1T09:00[America/Los_Angeles]
= yes
>> 2024-1-1 12:00[America/New_York] == Moment(2024, 1, 1, hour=9, timezone="America/Los_Angeles")
= yes
>> t := 2024-1-2 13:45[America/New_York]
>> t:after(days=40) == 2024-2-11T13:45:00[America/New_York]
= yes
>> t:date(timezone="America/New_York")
= "2024-01-02"
>> t:time(timezone="America/New_York")
= "1:45pm"
>> t:time(am_pm=no, timezone="America/New_York")
= "13:45"
>> t:relative(relative_to=t:after(minutes=65))
= "1 hour ago"
>> t:seconds_till(t:after(minutes=2))
= 120.
>> t:minutes_till(t:after(minutes=2))
= 2.
>> t:hours_till(t:after(minutes=60))
= 1.
>> t:day_of_week() # 1 = Sun, 2 = Mon, 3 = Tue
= 3
>> t:format("%A")
= "Tuesday"
>> t:unix_timestamp()
= Int64(1704221100)
>> t == Moment.from_unix_timestamp(1704221100)
= yes
>> t < t:after(minutes=1)
= yes
>> t < t:after(seconds=0.1)
= yes
>> now()

View File

@ -4,12 +4,6 @@ struct Foo(name:Text, next=none:@Foo)
enum MyEnum(Zero, One(x:Int), Two(x:Num, y:Text))
func main():
do:
>> obj := now()
>> bytes := obj:serialized()
>> deserialize(bytes -> Moment) == obj
= yes
do:
>> obj := Int64(123)
>> bytes := obj:serialized()