Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
@@ -0,0 +1,3 @@
|
||||
*.js
|
||||
*.log
|
||||
*.ppm
|
||||
@@ -0,0 +1,53 @@
|
||||
# 1 Billion Row Challenge (1BRC)
|
||||
|
||||
A solution to the [1 Billion Row Challenge](https://www.morling.dev/blog/one-billion-row-challenge/),
|
||||
written in [the V programming language](https://vlang.io/).
|
||||
|
||||
Read more about the challenge here: https://www.morling.dev/blog/one-billion-row-challenge/
|
||||
|
||||
|
||||
## Running instructions
|
||||
|
||||
Suggested compiler options for performance:
|
||||
|
||||
`v -cc gcc -prod -cflags "-std=c17 -march=native -mtune=native" .`
|
||||
|
||||
|
||||
### Step 1: Create a measurements file
|
||||
|
||||
Compile and run `make-samples` to create the sample file.
|
||||
|
||||
```
|
||||
cd make-samples
|
||||
v -cc gcc -prod -cflags "-std=c17 -march=native -mtune=native" .
|
||||
./make-samples 1000000000 > ~/measurements.txt
|
||||
```
|
||||
|
||||
NOTE: If you create a billion rows, the file will be about 12GB!
|
||||
|
||||
### Step 2: Run (and time) the solution
|
||||
|
||||
```
|
||||
cd solution
|
||||
v -cc gcc -prod -cflags "-std=c17 -march=native -mtune=native" .
|
||||
./solution ~/measurements.txt
|
||||
```
|
||||
|
||||
You can time the solution using `v time`:
|
||||
|
||||
`v time ./solution ~/measurements.txt`
|
||||
|
||||
By default, the solution runs in a single thread. If you want to run
|
||||
parallel processing, use the `-n` parameter, for example, to run with
|
||||
8 threads:
|
||||
|
||||
`./solution -n 8 ~/measurements.txt`
|
||||
|
||||
On Linux, to run one thread per core, use
|
||||
|
||||
`./solution -n $(nproc) ~/measurements.txt`
|
||||
|
||||
### Step 3: Improve upon the solution
|
||||
|
||||
Make changes that improve the performance and submit them.
|
||||
Let's show off what is possible in V!
|
||||
@@ -0,0 +1,413 @@
|
||||
Abha,18.0
|
||||
Abidjan,26.0
|
||||
Abéché,29.4
|
||||
Accra,26.4
|
||||
Addis Ababa,16.0
|
||||
Adelaide,17.3
|
||||
Aden,29.1
|
||||
Ahvaz,25.4
|
||||
Albuquerque,14.0
|
||||
Alexandra,11.0
|
||||
Alexandria,20.0
|
||||
Algiers,18.2
|
||||
Alice Springs,21.0
|
||||
Almaty,10.0
|
||||
Amsterdam,10.2
|
||||
Anadyr,-6.9
|
||||
Anchorage,2.8
|
||||
Andorra la Vella,9.8
|
||||
Ankara,12.0
|
||||
Antananarivo,17.9
|
||||
Antsiranana,25.2
|
||||
Arkhangelsk,1.3
|
||||
Ashgabat,17.1
|
||||
Asmara,15.6
|
||||
Assab,30.5
|
||||
Astana,3.5
|
||||
Athens,19.2
|
||||
Atlanta,17.0
|
||||
Auckland,15.2
|
||||
Austin,20.7
|
||||
Baghdad,22.77
|
||||
Baguio,19.5
|
||||
Baku,15.1
|
||||
Baltimore,13.1
|
||||
Bamako,27.8
|
||||
Bangkok,28.6
|
||||
Bangui,26.0
|
||||
Banjul,26.0
|
||||
Barcelona,18.2
|
||||
Bata,25.1
|
||||
Batumi,14.0
|
||||
Beijing,12.9
|
||||
Beirut,20.9
|
||||
Belgrade,12.5
|
||||
Belize City,26.7
|
||||
Benghazi,19.9
|
||||
Bergen,7.7
|
||||
Berlin,10.3
|
||||
Bilbao,14.7
|
||||
Birao,26.5
|
||||
Bishkek,11.3
|
||||
Bissau,27.0
|
||||
Blantyre,22.2
|
||||
Bloemfontein,15.6
|
||||
Boise,11.4
|
||||
Bordeaux,14.2
|
||||
Bosaso,30.0
|
||||
Boston,10.9
|
||||
Bouaké,26.0
|
||||
Bratislava,10.5
|
||||
Brazzaville,25.0
|
||||
Bridgetown,27.0
|
||||
Brisbane,21.4
|
||||
Brussels,10.5
|
||||
Bucharest,10.8
|
||||
Budapest,11.3
|
||||
Bujumbura,23.8
|
||||
Bulawayo,18.9
|
||||
Burnie,13.1
|
||||
Busan,15.0
|
||||
Cabo San Lucas,23.9
|
||||
Cairns,25.0
|
||||
Cairo,21.4
|
||||
Calgary,4.4
|
||||
Canberra,13.1
|
||||
Cape Town,16.2
|
||||
Changsha,17.4
|
||||
Charlotte,16.1
|
||||
Chiang Mai,25.8
|
||||
Chicago,9.8
|
||||
Chihuahua,18.6
|
||||
Chișinău,10.2
|
||||
Chittagong,25.9
|
||||
Chongqing,18.6
|
||||
Christchurch,12.2
|
||||
City of San Marino,11.8
|
||||
Colombo,27.4
|
||||
Columbus,11.7
|
||||
Conakry,26.4
|
||||
Copenhagen,9.1
|
||||
Cotonou,27.2
|
||||
Cracow,9.3
|
||||
Da Lat,17.9
|
||||
Da Nang,25.8
|
||||
Dakar,24.0
|
||||
Dallas,19.0
|
||||
Damascus,17.0
|
||||
Dampier,26.4
|
||||
Dar es Salaam,25.8
|
||||
Darwin,27.6
|
||||
Denpasar,23.7
|
||||
Denver,10.4
|
||||
Detroit,10.0
|
||||
Dhaka,25.9
|
||||
Dikson,-11.1
|
||||
Dili,26.6
|
||||
Djibouti,29.9
|
||||
Dodoma,22.7
|
||||
Dolisie,24.0
|
||||
Douala,26.7
|
||||
Dubai,26.9
|
||||
Dublin,9.8
|
||||
Dunedin,11.1
|
||||
Durban,20.6
|
||||
Dushanbe,14.7
|
||||
Edinburgh,9.3
|
||||
Edmonton,4.2
|
||||
El Paso,18.1
|
||||
Entebbe,21.0
|
||||
Erbil,19.5
|
||||
Erzurum,5.1
|
||||
Fairbanks,-2.3
|
||||
Fianarantsoa,17.9
|
||||
Flores, Petén,26.4
|
||||
Frankfurt,10.6
|
||||
Fresno,17.9
|
||||
Fukuoka,17.0
|
||||
Gabès,19.5
|
||||
Gaborone,21.0
|
||||
Gagnoa,26.0
|
||||
Gangtok,15.2
|
||||
Garissa,29.3
|
||||
Garoua,28.3
|
||||
George Town,27.9
|
||||
Ghanzi,21.4
|
||||
Gjoa Haven,-14.4
|
||||
Guadalajara,20.9
|
||||
Guangzhou,22.4
|
||||
Guatemala City,20.4
|
||||
Halifax,7.5
|
||||
Hamburg,9.7
|
||||
Hamilton,13.8
|
||||
Hanga Roa,20.5
|
||||
Hanoi,23.6
|
||||
Harare,18.4
|
||||
Harbin,5.0
|
||||
Hargeisa,21.7
|
||||
Hat Yai,27.0
|
||||
Havana,25.2
|
||||
Helsinki,5.9
|
||||
Heraklion,18.9
|
||||
Hiroshima,16.3
|
||||
Ho Chi Minh City,27.4
|
||||
Hobart,12.7
|
||||
Hong Kong,23.3
|
||||
Honiara,26.5
|
||||
Honolulu,25.4
|
||||
Houston,20.8
|
||||
Ifrane,11.4
|
||||
Indianapolis,11.8
|
||||
Iqaluit,-9.3
|
||||
Irkutsk,1.0
|
||||
Istanbul,13.9
|
||||
İzmir,17.9
|
||||
Jacksonville,20.3
|
||||
Jakarta,26.7
|
||||
Jayapura,27.0
|
||||
Jerusalem,18.3
|
||||
Johannesburg,15.5
|
||||
Jos,22.8
|
||||
Juba,27.8
|
||||
Kabul,12.1
|
||||
Kampala,20.0
|
||||
Kandi,27.7
|
||||
Kankan,26.5
|
||||
Kano,26.4
|
||||
Kansas City,12.5
|
||||
Karachi,26.0
|
||||
Karonga,24.4
|
||||
Kathmandu,18.3
|
||||
Khartoum,29.9
|
||||
Kingston,27.4
|
||||
Kinshasa,25.3
|
||||
Kolkata,26.7
|
||||
Kuala Lumpur,27.3
|
||||
Kumasi,26.0
|
||||
Kunming,15.7
|
||||
Kuopio,3.4
|
||||
Kuwait City,25.7
|
||||
Kyiv,8.4
|
||||
Kyoto,15.8
|
||||
La Ceiba,26.2
|
||||
La Paz,23.7
|
||||
Lagos,26.8
|
||||
Lahore,24.3
|
||||
Lake Havasu City,23.7
|
||||
Lake Tekapo,8.7
|
||||
Las Palmas de Gran Canaria,21.2
|
||||
Las Vegas,20.3
|
||||
Launceston,13.1
|
||||
Lhasa,7.6
|
||||
Libreville,25.9
|
||||
Lisbon,17.5
|
||||
Livingstone,21.8
|
||||
Ljubljana,10.9
|
||||
Lodwar,29.3
|
||||
Lomé,26.9
|
||||
London,11.3
|
||||
Los Angeles,18.6
|
||||
Louisville,13.9
|
||||
Luanda,25.8
|
||||
Lubumbashi,20.8
|
||||
Lusaka,19.9
|
||||
Luxembourg City,9.3
|
||||
Lviv,7.8
|
||||
Lyon,12.5
|
||||
Madrid,15.0
|
||||
Mahajanga,26.3
|
||||
Makassar,26.7
|
||||
Makurdi,26.0
|
||||
Malabo,26.3
|
||||
Malé,28.0
|
||||
Managua,27.3
|
||||
Manama,26.5
|
||||
Mandalay,28.0
|
||||
Mango,28.1
|
||||
Manila,28.4
|
||||
Maputo,22.8
|
||||
Marrakesh,19.6
|
||||
Marseille,15.8
|
||||
Maun,22.4
|
||||
Medan,26.5
|
||||
Mek'ele,22.7
|
||||
Melbourne,15.1
|
||||
Memphis,17.2
|
||||
Mexicali,23.1
|
||||
Mexico City,17.5
|
||||
Miami,24.9
|
||||
Milan,13.0
|
||||
Milwaukee,8.9
|
||||
Minneapolis,7.8
|
||||
Minsk,6.7
|
||||
Mogadishu,27.1
|
||||
Mombasa,26.3
|
||||
Monaco,16.4
|
||||
Moncton,6.1
|
||||
Monterrey,22.3
|
||||
Montreal,6.8
|
||||
Moscow,5.8
|
||||
Mumbai,27.1
|
||||
Murmansk,0.6
|
||||
Muscat,28.0
|
||||
Mzuzu,17.7
|
||||
N'Djamena,28.3
|
||||
Naha,23.1
|
||||
Nairobi,17.8
|
||||
Nakhon Ratchasima,27.3
|
||||
Napier,14.6
|
||||
Napoli,15.9
|
||||
Nashville,15.4
|
||||
Nassau,24.6
|
||||
Ndola,20.3
|
||||
New Delhi,25.0
|
||||
New Orleans,20.7
|
||||
New York City,12.9
|
||||
Ngaoundéré,22.0
|
||||
Niamey,29.3
|
||||
Nicosia,19.7
|
||||
Niigata,13.9
|
||||
Nouadhibou,21.3
|
||||
Nouakchott,25.7
|
||||
Novosibirsk,1.7
|
||||
Nuuk,-1.4
|
||||
Odesa,10.7
|
||||
Odienné,26.0
|
||||
Oklahoma City,15.9
|
||||
Omaha,10.6
|
||||
Oranjestad,28.1
|
||||
Oslo,5.7
|
||||
Ottawa,6.6
|
||||
Ouagadougou,28.3
|
||||
Ouahigouya,28.6
|
||||
Ouarzazate,18.9
|
||||
Oulu,2.7
|
||||
Palembang,27.3
|
||||
Palermo,18.5
|
||||
Palm Springs,24.5
|
||||
Palmerston North,13.2
|
||||
Panama City,28.0
|
||||
Parakou,26.8
|
||||
Paris,12.3
|
||||
Perth,18.7
|
||||
Petropavlovsk-Kamchatsky,1.9
|
||||
Philadelphia,13.2
|
||||
Phnom Penh,28.3
|
||||
Phoenix,23.9
|
||||
Pittsburgh,10.8
|
||||
Podgorica,15.3
|
||||
Pointe-Noire,26.1
|
||||
Pontianak,27.7
|
||||
Port Moresby,26.9
|
||||
Port Sudan,28.4
|
||||
Port Vila,24.3
|
||||
Port-Gentil,26.0
|
||||
Portland {OR},12.4
|
||||
Porto,15.7
|
||||
Prague,8.4
|
||||
Praia,24.4
|
||||
Pretoria,18.2
|
||||
Pyongyang,10.8
|
||||
Rabat,17.2
|
||||
Rangpur,24.4
|
||||
Reggane,28.3
|
||||
Reykjavík,4.3
|
||||
Riga,6.2
|
||||
Riyadh,26.0
|
||||
Rome,15.2
|
||||
Roseau,26.2
|
||||
Rostov-on-Don,9.9
|
||||
Sacramento,16.3
|
||||
Saint Petersburg,5.8
|
||||
Saint-Pierre,5.7
|
||||
Salt Lake City,11.6
|
||||
San Antonio,20.8
|
||||
San Diego,17.8
|
||||
San Francisco,14.6
|
||||
San Jose,16.4
|
||||
San José,22.6
|
||||
San Juan,27.2
|
||||
San Salvador,23.1
|
||||
Sana'a,20.0
|
||||
Santo Domingo,25.9
|
||||
Sapporo,8.9
|
||||
Sarajevo,10.1
|
||||
Saskatoon,3.3
|
||||
Seattle,11.3
|
||||
Ségou,28.0
|
||||
Seoul,12.5
|
||||
Seville,19.2
|
||||
Shanghai,16.7
|
||||
Singapore,27.0
|
||||
Skopje,12.4
|
||||
Sochi,14.2
|
||||
Sofia,10.6
|
||||
Sokoto,28.0
|
||||
Split,16.1
|
||||
St. John's,5.0
|
||||
St. Louis,13.9
|
||||
Stockholm,6.6
|
||||
Surabaya,27.1
|
||||
Suva,25.6
|
||||
Suwałki,7.2
|
||||
Sydney,17.7
|
||||
Tabora,23.0
|
||||
Tabriz,12.6
|
||||
Taipei,23.0
|
||||
Tallinn,6.4
|
||||
Tamale,27.9
|
||||
Tamanrasset,21.7
|
||||
Tampa,22.9
|
||||
Tashkent,14.8
|
||||
Tauranga,14.8
|
||||
Tbilisi,12.9
|
||||
Tegucigalpa,21.7
|
||||
Tehran,17.0
|
||||
Tel Aviv,20.0
|
||||
Thessaloniki,16.0
|
||||
Thiès,24.0
|
||||
Tijuana,17.8
|
||||
Timbuktu,28.0
|
||||
Tirana,15.2
|
||||
Toamasina,23.4
|
||||
Tokyo,15.4
|
||||
Toliara,24.1
|
||||
Toluca,12.4
|
||||
Toronto,9.4
|
||||
Tripoli,20.0
|
||||
Tromsø,2.9
|
||||
Tucson,20.9
|
||||
Tunis,18.4
|
||||
Ulaanbaatar,-0.4
|
||||
Upington,20.4
|
||||
Ürümqi,7.4
|
||||
Vaduz,10.1
|
||||
Valencia,18.3
|
||||
Valletta,18.8
|
||||
Vancouver,10.4
|
||||
Veracruz,25.4
|
||||
Vienna,10.4
|
||||
Vientiane,25.9
|
||||
Villahermosa,27.1
|
||||
Vilnius,6.0
|
||||
Virginia Beach,15.8
|
||||
Vladivostok,4.9
|
||||
Warsaw,8.5
|
||||
Washington, D.C.,14.6
|
||||
Wau,27.8
|
||||
Wellington,12.9
|
||||
Whitehorse,-0.1
|
||||
Wichita,13.9
|
||||
Willemstad,28.0
|
||||
Winnipeg,3.0
|
||||
Wrocław,9.6
|
||||
Xi'an,14.1
|
||||
Yakutsk,-8.8
|
||||
Yangon,27.5
|
||||
Yaoundé,23.8
|
||||
Yellowknife,-4.3
|
||||
Yerevan,12.4
|
||||
Yinchuan,9.0
|
||||
Zagreb,10.7
|
||||
Zanzibar City,26.0
|
||||
Zürich,9.3
|
||||
@@ -0,0 +1,40 @@
|
||||
import encoding.csv
|
||||
import flag
|
||||
import os
|
||||
import rand
|
||||
|
||||
struct CityMean {
|
||||
city string
|
||||
mean f64
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
fp.version('1brc sample generator v1.0.0')
|
||||
fp.skip_executable()
|
||||
fp.application('Sample generator for 1 billion rows challenge')
|
||||
fp.description('The 1 billion rows challenge solved in V.\nFor details, see https://www.morling.dev/blog/one-billion-row-challenge/')
|
||||
input_file := fp.string('city-file', `i`, 'cities.txt',
|
||||
'Path to input file with cities and means list')
|
||||
fp.limit_free_args_to_exactly(1)!
|
||||
sample_count := fp.remaining_parameters()[0].u64()
|
||||
|
||||
content := os.read_file(input_file) or { panic(err) }
|
||||
mut reader := csv.new_reader(content, csv.ReaderConfig{ delimiter: `,` })
|
||||
mut means := []CityMean{}
|
||||
for {
|
||||
rec := reader.read() or { break }
|
||||
means << CityMean{
|
||||
city: rec[0]
|
||||
mean: rec[1].f64()
|
||||
}
|
||||
}
|
||||
|
||||
for _ in 0 .. sample_count / 2 {
|
||||
mut city := rand.intn(means.len)!
|
||||
m1, m2 := rand.normal_pair(mu: means[city].mean, sigma: 10)!
|
||||
println('${means[city].city};${m1:.1f}')
|
||||
city = rand.intn(means.len)!
|
||||
println('${means[city].city};${m2:.1f}')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,202 @@
|
||||
// vtest build: !windows // requires mmap
|
||||
import flag
|
||||
import math
|
||||
import os
|
||||
|
||||
#include <sys/mman.h>
|
||||
|
||||
fn C.mmap(addr voidptr, len u64, prot i32, flags i32, fd i32, offset i64) voidptr
|
||||
fn C.munmap(addr voidptr, len u64) i32
|
||||
|
||||
struct MemoryMappedFile {
|
||||
size u64
|
||||
mut:
|
||||
data &u8
|
||||
file os.File
|
||||
}
|
||||
|
||||
fn mmap_file(path string) MemoryMappedFile {
|
||||
mut mf := MemoryMappedFile{
|
||||
file: os.open_file(path, 'r', 0) or { panic('fail') }
|
||||
size: os.file_size(path)
|
||||
data: C.NULL
|
||||
}
|
||||
|
||||
mf.data = &u8(C.mmap(C.NULL, mf.size, C.PROT_READ, C.MAP_SHARED, mf.file.fd, 0))
|
||||
return mf
|
||||
}
|
||||
|
||||
fn (mut mf MemoryMappedFile) unmap() {
|
||||
if C.munmap(mf.data, mf.size) != 0 {
|
||||
panic('(${C.errno}) munmap() failed')
|
||||
}
|
||||
mf.file.close()
|
||||
}
|
||||
|
||||
enum ReadState {
|
||||
city
|
||||
temp
|
||||
}
|
||||
|
||||
struct Result {
|
||||
pub mut:
|
||||
min i32
|
||||
max i32
|
||||
sum i32
|
||||
count u32
|
||||
}
|
||||
|
||||
fn format_value(value i32) string {
|
||||
return '${value / 10}.${math.abs(value % 10)}'
|
||||
}
|
||||
|
||||
fn print_results(results map[string]Result, print_nicely bool) {
|
||||
mut output := []string{cap: results.len}
|
||||
mut cities := results.keys()
|
||||
cities.sort()
|
||||
for city in cities {
|
||||
v := results[city]
|
||||
mean := f64(v.sum) / v.count / 10
|
||||
output << '${city}=${format_value(v.min)}/${mean:.1f}/${format_value(v.max)}'
|
||||
}
|
||||
if print_nicely {
|
||||
println(output.join('\n'))
|
||||
} else {
|
||||
println('{' + output.join(', ') + '}')
|
||||
}
|
||||
}
|
||||
|
||||
fn combine_results(results []map[string]Result) map[string]Result {
|
||||
mut combined_result := map[string]Result{}
|
||||
for result in results {
|
||||
for city, r in result {
|
||||
if city !in combined_result {
|
||||
combined_result[city] = r
|
||||
} else {
|
||||
if r.max > combined_result[city].max {
|
||||
combined_result[city].max = r.max
|
||||
}
|
||||
if r.min < combined_result[city].min {
|
||||
combined_result[city].min = r.min
|
||||
}
|
||||
combined_result[city].sum += r.sum
|
||||
combined_result[city].count += r.count
|
||||
}
|
||||
}
|
||||
}
|
||||
return combined_result
|
||||
}
|
||||
|
||||
@[direct_array_access]
|
||||
fn process_chunk(addr &u8, from u64, to u64) map[string]Result {
|
||||
mut results := map[string]Result{}
|
||||
mut state := ReadState.city
|
||||
mut city := ''
|
||||
mut temp := i32(0)
|
||||
mut mod := i32(1)
|
||||
mut j := int(0)
|
||||
for i in from .. to {
|
||||
c := unsafe { u8(addr[i]) }
|
||||
match state {
|
||||
.city {
|
||||
match c {
|
||||
`;` {
|
||||
state = .temp
|
||||
city = unsafe { tos(addr[i - u64(j)], j) }
|
||||
}
|
||||
else {
|
||||
j += 1
|
||||
}
|
||||
}
|
||||
}
|
||||
.temp {
|
||||
match c {
|
||||
`\n` {
|
||||
temp *= mod
|
||||
if city !in results {
|
||||
results[city] = Result{
|
||||
min: temp
|
||||
max: temp
|
||||
sum: temp
|
||||
count: 1
|
||||
}
|
||||
} else {
|
||||
if temp > results[city].max {
|
||||
results[city].max = temp
|
||||
}
|
||||
if temp < results[city].min {
|
||||
results[city].min = temp
|
||||
}
|
||||
results[city].sum += temp
|
||||
results[city].count += 1
|
||||
}
|
||||
state = .city
|
||||
temp = 0
|
||||
mod = 1
|
||||
j = 0
|
||||
}
|
||||
`-` {
|
||||
mod = -1
|
||||
}
|
||||
`.` {}
|
||||
else {
|
||||
// ASCII 48 = '0' ... ASCII 57 = '9' => (ASCII value) - 48 = decimal value
|
||||
temp = temp * 10 + (c - 48)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return results
|
||||
}
|
||||
|
||||
fn process_in_parallel(mf MemoryMappedFile, thread_count u32) map[string]Result {
|
||||
mut threads := []thread map[string]Result{}
|
||||
approx_chunk_size := mf.size / thread_count
|
||||
mut from := u64(0)
|
||||
mut to := approx_chunk_size
|
||||
for _ in 0 .. thread_count - 1 {
|
||||
unsafe {
|
||||
for mf.data[to] != `\n` {
|
||||
to += 1
|
||||
}
|
||||
}
|
||||
threads << spawn process_chunk(mf.data, from, to)
|
||||
from = to + 1
|
||||
to = from + approx_chunk_size
|
||||
}
|
||||
to = mf.size
|
||||
threads << spawn process_chunk(mf.data, from, to)
|
||||
res := threads.wait()
|
||||
return combine_results(res)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
fp.version('1brc v1.0.0')
|
||||
fp.skip_executable()
|
||||
fp.application('1 billion rows challenge')
|
||||
fp.description('The 1 billion rows challenge solved in V.\nFor details, see https://www.morling.dev/blog/one-billion-row-challenge/')
|
||||
thread_count := u32(fp.int('threads', `n`, 1, 'number of threads for parallel processing.'))
|
||||
print_nicely := fp.bool('human-readable', `h`, false,
|
||||
'Print results with new lines rather than following challenge spec')
|
||||
quiet := fp.bool('quiet', `q`, false,
|
||||
'Suppress the results output (e.g., if you only care about timing)')
|
||||
fp.limit_free_args_to_exactly(1)!
|
||||
path := fp.remaining_parameters()[0]
|
||||
|
||||
mut mf := mmap_file(path)
|
||||
defer {
|
||||
mf.unmap()
|
||||
}
|
||||
|
||||
results := if thread_count > 1 {
|
||||
process_in_parallel(mf, thread_count)
|
||||
} else {
|
||||
process_chunk(mf.data, 0, mf.size)
|
||||
}
|
||||
|
||||
if !quiet {
|
||||
print_results(results, print_nicely)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
2048.wasm
|
||||
2048
|
||||
main
|
||||
|
||||
index.js
|
||||
index.wasm
|
||||
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,21 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2020 Delyan Angelov
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
||||
@@ -0,0 +1,62 @@
|
||||
# V 2048
|
||||
|
||||
This is a simple 2048 game, written in [the V programming language](https://vlang.io/).
|
||||
|
||||
WebAssembly demo: https://v2048.vercel.app
|
||||
|
||||

|
||||
|
||||
## Description
|
||||
|
||||
Merge tiles by moving them.
|
||||
After each move, a new random tile is added (2 or 4).
|
||||
The goal of the game is to create a tile with a value of 2048.
|
||||
|
||||
## Keys
|
||||
|
||||
Escape - exit the game
|
||||
Backspace - undo last move
|
||||
n - restart the game
|
||||
t - toggle the UI theme
|
||||
Enter - toggle the tile text format
|
||||
|
||||
UP,LEFT,DOWN,RIGHT / W,A,S,D / touchscreen swipes - move the tiles
|
||||
|
||||
## Running instructions
|
||||
|
||||
Compile & run the game with `./v run examples/2048`
|
||||
|
||||
## Compiling to WASM
|
||||
|
||||
1. Install Emscripten from:
|
||||
https://emscripten.org/docs/getting_started/downloads.html
|
||||
|
||||
2. Make sure that the environment in your shell is setup correctly,
|
||||
i.e. that `emcc --version` works.
|
||||
|
||||
```sh
|
||||
. /opt/emsdk/emsdk_env.sh
|
||||
emcc --version
|
||||
```
|
||||
|
||||
3. Compile the game to WASM:
|
||||
(the JS file contains a loader for the .wasm file, without the extension):
|
||||
```sh
|
||||
v -prod -os wasm32_emscripten -o examples/2048/index.js examples/2048/
|
||||
```
|
||||
|
||||
5. Run/test the game:
|
||||
|
||||
```sh
|
||||
emrun examples/2048/index.html
|
||||
```
|
||||
|
||||
If you prefer, instead of `emrun`, you can start a simple V http file server:
|
||||
```sh
|
||||
v -e 'import net.http.file; file.serve(folder: "examples/2048")'
|
||||
```
|
||||
and then open http://127.0.0.1:4001/ in your browser.
|
||||
|
||||
Once you have opened the game in your browser, you can make changes to
|
||||
the V source, then recompile (step 3), and refresh the browser window
|
||||
with the game, to see their effect.
|
||||
Binary file not shown.
|
After Width: | Height: | Size: 19 KiB |
@@ -0,0 +1,36 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<meta content="text/html; charset=utf-8" http-equiv=Content-Type>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>v 2048</title>
|
||||
<style>
|
||||
body {
|
||||
margin-left: 1rem;
|
||||
background-color: rgb(250, 248, 239);
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
|
||||
<body>
|
||||
<h1>V 2048 demo</h1>
|
||||
<h3>
|
||||
Authors: <a href="http://github.com/spaceface777">@spaceface777</a>, <a href="http://github.com/spytheman">@spytheman</a> and other V contributors.
|
||||
<br>
|
||||
Original game by <a href="http://github.com/gabrielecirulli">@gabrielecirulli</a>.
|
||||
</h3>
|
||||
<h3>Click on the canvas to go into fullscreen mode</h3>
|
||||
<canvas id=canvas oncontextmenu=event.preventDefault() tabindex=-1 width="544" height="560"></canvas>
|
||||
<script src=index.js></script>
|
||||
<script>
|
||||
const m = Module()
|
||||
const e = document.getElementById("canvas");
|
||||
e.onclick = e.ontouchstart = () => {
|
||||
e.requestFullscreen()
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
|
||||
</html>
|
||||
@@ -0,0 +1,7 @@
|
||||
Module {
|
||||
name: 'v2048',
|
||||
description: 'A simple 2048 game written in V.',
|
||||
version: '0.0.2',
|
||||
repo_url: 'https://github.com/spytheman/v2048',
|
||||
dependencies: []
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
import term.ui as tui
|
||||
import flag
|
||||
|
||||
struct DocTest {
|
||||
show_version bool @[short: v; xdoc: 'Show version and exit']
|
||||
debug_level int @[long: debug; short: d; xdoc: 'Debug level']
|
||||
level f32 @[only: l; xdoc: 'Do not show this']
|
||||
example string
|
||||
square bool
|
||||
multi int @[only: m; repeats]
|
||||
wroom []int @[short: w]
|
||||
the_limit string
|
||||
}
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
tui &tui.Context = unsafe { nil }
|
||||
frame int
|
||||
square string = '.____.\n| |\n| |\n|____|'
|
||||
pad int = 1
|
||||
direction int = 1
|
||||
}
|
||||
|
||||
fn event(e &tui.Event, mut _app App) {
|
||||
match e.typ {
|
||||
.mouse_down {}
|
||||
.mouse_drag {}
|
||||
.mouse_up {}
|
||||
.key_down {
|
||||
if e.code == .c {
|
||||
} else if e.code == .escape {
|
||||
exit(0)
|
||||
}
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
|
||||
fn frame(mut app App) {
|
||||
app.tui.clear()
|
||||
app.frame++
|
||||
|
||||
if app.frame % 4 == 0 {
|
||||
app.pad += app.direction
|
||||
app.square = '${' '.repeat(app.pad)}.____.\n${' '.repeat(app.pad)}| |\n${' '.repeat(app.pad)}| |\n${' '.repeat(app.pad)}|____|'
|
||||
}
|
||||
if app.frame % 100 == 0 {
|
||||
if app.direction > 0 {
|
||||
app.direction = -1
|
||||
} else {
|
||||
app.direction = 1
|
||||
}
|
||||
}
|
||||
|
||||
help_text := flag.to_doc[DocTest](
|
||||
version: '1.0'
|
||||
description: 'Hello! This should show an *animated* example application description.
|
||||
We are at frame ${app.frame}.
|
||||
Press ESCAPE or Ctrl+C to exit'
|
||||
footer: '
|
||||
Press ESCAPE or Ctrl+C to exit'
|
||||
fields: {
|
||||
'level': 'Level of lorem ipsum\nand more\nmany many many more.\nNotice how user newlines/format is kept since\ninput lines are all less or within\nthe default layout.description_padding\nand max width'
|
||||
'example': 'Looong example text without newlines or anything else and lorem ipsum and more and many many many more. Should be auto fitted'
|
||||
'multi': 'This flag can be repeated'
|
||||
'-e, --extra': 'Secret flag that does not exist on the struct, but we want documented (in same format as the others)'
|
||||
'-q, --quiet-and-quite-long-flag <string>': 'Mega long description and secret flag that does not exist on the struct, but we want documented. Also the flag has custom newlines\nand the flag line itself is super long'
|
||||
'square': '${app.square}'
|
||||
}
|
||||
) or { '' }
|
||||
|
||||
app.tui.draw_text(0, 0, '${help_text}')
|
||||
app.tui.reset()
|
||||
|
||||
app.tui.flush()
|
||||
}
|
||||
|
||||
type EventFn = fn (&tui.Event, voidptr)
|
||||
|
||||
type FrameFn = fn (voidptr)
|
||||
|
||||
fn main() {
|
||||
mut app := &App{}
|
||||
app.tui = tui.init(
|
||||
user_data: app
|
||||
event_fn: EventFn(event)
|
||||
frame_fn: FrameFn(frame)
|
||||
hide_cursor: true
|
||||
frame_rate: 60
|
||||
)
|
||||
app.tui.run()!
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import archive.tar
|
||||
import flag
|
||||
import net.http
|
||||
import os
|
||||
import term
|
||||
|
||||
const default_url = 'https://github.com/vlang/v/archive/refs/tags/v0.1.3.tar.gz'
|
||||
|
||||
@[heap]
|
||||
struct Context {
|
||||
url string // Web starting with http:// or https://. Local starting with file:///
|
||||
chunks bool // true: decompress with callback
|
||||
debug int // print debug lines
|
||||
max_blocks int // if max_blocks > 0 and is reached stops early.
|
||||
filename string // if filename is found as a path of a data block, stops early.
|
||||
}
|
||||
|
||||
fn (ctx &Context) read_last_block(mut read tar.Read) bool {
|
||||
if ctx.max_blocks > 0 && ctx.max_blocks < read.get_block_number() {
|
||||
read.stop_early = true
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fn new_context() !&Context {
|
||||
mut fp := flag.new_flag_parser(os.args)
|
||||
fp.application('tar_gz_reader')
|
||||
fp.version('0.0.20250721')
|
||||
fp.description('Reads into memory selected sections of *.tar.gz. archives from https or home_dir.')
|
||||
fp.skip_executable()
|
||||
ctx := &Context{
|
||||
url: fp.string('url', `u`, default_url,
|
||||
'archive *.tar.gz URL, default(${default_url}). Start name with file:/// for local')
|
||||
chunks: fp.bool('chunks', `c`, false,
|
||||
'decompress with chunks to reduce RAM usage, default(false)')
|
||||
debug: fp.int('debug', `d`, 0,
|
||||
'prints blocks: 1=other, 2:+dirs, 3=+files, 4=+data, default(0=silent)')
|
||||
max_blocks: fp.int('max_blocks', `m`, 0,
|
||||
'maximum blocks to read, stop early. Default(0=read all)')
|
||||
filename: fp.string('filename', `f`, '',
|
||||
'filename content complete print, stop early. Default(empty means none)')
|
||||
}
|
||||
additional := fp.finalize()!
|
||||
if additional.len > 0 {
|
||||
println('unprocessed args ${additional.join_lines()}')
|
||||
}
|
||||
return ctx
|
||||
}
|
||||
|
||||
// Downloader downloads a *.tar.gz using HTTP chunks
|
||||
struct Downloader {
|
||||
mut:
|
||||
chunks int
|
||||
data []u8
|
||||
}
|
||||
|
||||
fn new_downloader(url string) !&Downloader {
|
||||
mut downloader := &Downloader{}
|
||||
params := http.DownloaderParams{
|
||||
downloader: downloader
|
||||
}
|
||||
if url.starts_with('http://') || url.starts_with('https://') {
|
||||
http.download_file_with_progress(url, '', params)!
|
||||
} else if url.starts_with('file:///') {
|
||||
path := '${os.home_dir()}/${url[8..]}'
|
||||
println('path ${path}')
|
||||
downloader.data = os.read_bytes(path)!
|
||||
}
|
||||
return downloader
|
||||
}
|
||||
|
||||
fn (mut d Downloader) on_start(mut _request http.Request, _path string) ! {}
|
||||
|
||||
fn (mut d Downloader) on_chunk(_request &http.Request, chunk []u8, _already_received u64, expected u64) ! {
|
||||
if expected == 0 {
|
||||
return
|
||||
}
|
||||
d.chunks++
|
||||
d.data << chunk
|
||||
}
|
||||
|
||||
fn (mut d Downloader) on_finish(_request &http.Request, _response &http.Response) ! {}
|
||||
|
||||
struct FileReader implements tar.Reader {
|
||||
ctx &Context
|
||||
mut:
|
||||
filepath string
|
||||
content []u8
|
||||
}
|
||||
|
||||
fn (mut f FileReader) other_block(mut read tar.Read, details string) {
|
||||
if f.ctx.read_last_block(mut read) {
|
||||
return
|
||||
}
|
||||
if f.ctx.debug > 0 {
|
||||
row := 'OTHER block:${read.get_block_number():6} ${read.get_special()} ${details} ${read.get_path()} '
|
||||
println(term.colorize(term.bright_yellow, row))
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut f FileReader) dir_block(mut read tar.Read, size u64) {
|
||||
if f.ctx.read_last_block(mut read) {
|
||||
return
|
||||
}
|
||||
if f.ctx.debug > 1 {
|
||||
row := 'DIR block:${read.get_block_number():6} ${read.get_path()} size:${size}'
|
||||
println(term.colorize(term.green, row))
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut f FileReader) file_block(mut read tar.Read, size u64) {
|
||||
if f.ctx.read_last_block(mut read) {
|
||||
return
|
||||
}
|
||||
path := read.get_path()
|
||||
if f.ctx.debug > 2 {
|
||||
row := ' FILE block:${read.get_block_number():6} ${path} size:${size}'
|
||||
println(term.colorize(term.bright_blue, row))
|
||||
}
|
||||
if f.ctx.filename != '' && f.filepath == '' && path.ends_with(f.ctx.filename) {
|
||||
f.filepath = path
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut f FileReader) data_block(mut read tar.Read, data []u8, pending int) {
|
||||
if f.ctx.read_last_block(mut read) {
|
||||
return
|
||||
}
|
||||
path := read.get_path()
|
||||
if f.ctx.debug > 3 {
|
||||
println(' DATA block:${read.get_block_number():6} ${path} len:${data.len} pend:${pending}')
|
||||
}
|
||||
if f.ctx.filename != '' {
|
||||
if f.filepath == path {
|
||||
f.content << data
|
||||
if pending == 0 {
|
||||
// our file of interest data is complete
|
||||
read.stop_early = true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
ctx := new_context()!
|
||||
reader := FileReader{
|
||||
ctx: ctx
|
||||
}
|
||||
mut untar := tar.new_untar(reader)
|
||||
mut decompressor := tar.new_decompressor(untar)
|
||||
downloader := new_downloader(ctx.url)!
|
||||
if ctx.chunks {
|
||||
decompressor.read_chunks(downloader.data)!
|
||||
} else {
|
||||
decompressor.read_all(downloader.data)!
|
||||
}
|
||||
println('-'.repeat(80))
|
||||
println('Download: ${ctx.url} chunks:${downloader.chunks} bytes=${downloader.data.len}')
|
||||
println('Untar: ${untar}')
|
||||
println('Content: Path:${reader.filepath} bytes:${reader.content.len}')
|
||||
println('-'.repeat(80))
|
||||
println('${reader.content.bytestr()}')
|
||||
println('-'.repeat(80))
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// vtest build: !msvc
|
||||
fn main() {
|
||||
a := 100
|
||||
b := 20
|
||||
mut c := 0
|
||||
$if amd64 {
|
||||
asm amd64 {
|
||||
mov eax, a
|
||||
add eax, b
|
||||
mov c, eax
|
||||
; =r (c) // output
|
||||
; r (a) // input
|
||||
r (b)
|
||||
}
|
||||
}
|
||||
println('a: ${a}') // 100
|
||||
println('b: ${b}') // 20
|
||||
println('c: ${c}') // 120
|
||||
}
|
||||
Binary file not shown.
Binary file not shown.
@@ -0,0 +1,202 @@
|
||||
|
||||
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 [yyyy] [name of copyright owner]
|
||||
|
||||
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.
|
||||
@@ -0,0 +1,93 @@
|
||||
Copyright (c) 2012, Eduardo Tunni (http://www.tipo.net.ar), with Reserved Font Name "Imprima"
|
||||
|
||||
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||
This license is copied below, and is also available with a FAQ at:
|
||||
http://scripts.sil.org/OFL
|
||||
|
||||
|
||||
-----------------------------------------------------------
|
||||
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||
-----------------------------------------------------------
|
||||
|
||||
PREAMBLE
|
||||
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||
development of collaborative font projects, to support the font creation
|
||||
efforts of academic and linguistic communities, and to provide a free and
|
||||
open framework in which fonts may be shared and improved in partnership
|
||||
with others.
|
||||
|
||||
The OFL allows the licensed fonts to be used, studied, modified and
|
||||
redistributed freely as long as they are not sold by themselves. The
|
||||
fonts, including any derivative works, can be bundled, embedded,
|
||||
redistributed and/or sold with any software provided that any reserved
|
||||
names are not used by derivative works. The fonts and derivatives,
|
||||
however, cannot be released under any other type of license. The
|
||||
requirement for fonts to remain under this license does not apply
|
||||
to any document created using the fonts or their derivatives.
|
||||
|
||||
DEFINITIONS
|
||||
"Font Software" refers to the set of files released by the Copyright
|
||||
Holder(s) under this license and clearly marked as such. This may
|
||||
include source files, build scripts and documentation.
|
||||
|
||||
"Reserved Font Name" refers to any names specified as such after the
|
||||
copyright statement(s).
|
||||
|
||||
"Original Version" refers to the collection of Font Software components as
|
||||
distributed by the Copyright Holder(s).
|
||||
|
||||
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||
or substituting -- in part or in whole -- any of the components of the
|
||||
Original Version, by changing formats or by porting the Font Software to a
|
||||
new environment.
|
||||
|
||||
"Author" refers to any designer, engineer, programmer, technical
|
||||
writer or other person who contributed to the Font Software.
|
||||
|
||||
PERMISSION & CONDITIONS
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||
redistribute, and sell modified and unmodified copies of the Font
|
||||
Software, subject to the following conditions:
|
||||
|
||||
1) Neither the Font Software nor any of its individual components,
|
||||
in Original or Modified Versions, may be sold by itself.
|
||||
|
||||
2) Original or Modified Versions of the Font Software may be bundled,
|
||||
redistributed and/or sold with any software, provided that each copy
|
||||
contains the above copyright notice and this license. These can be
|
||||
included either as stand-alone text files, human-readable headers or
|
||||
in the appropriate machine-readable metadata fields within text or
|
||||
binary files as long as those fields can be easily viewed by the user.
|
||||
|
||||
3) No Modified Version of the Font Software may use the Reserved Font
|
||||
Name(s) unless explicit written permission is granted by the corresponding
|
||||
Copyright Holder. This restriction only applies to the primary font name as
|
||||
presented to the users.
|
||||
|
||||
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||
Software shall not be used to promote, endorse or advertise any
|
||||
Modified Version, except to acknowledge the contribution(s) of the
|
||||
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||
permission.
|
||||
|
||||
5) The Font Software, modified or unmodified, in part or in whole,
|
||||
must be distributed entirely under this license, and must not be
|
||||
distributed under any other license. The requirement for fonts to
|
||||
remain under this license does not apply to any document created
|
||||
using the Font Software.
|
||||
|
||||
TERMINATION
|
||||
This license becomes null and void if any of the above conditions are
|
||||
not met.
|
||||
|
||||
DISCLAIMER
|
||||
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||
Binary file not shown.
Binary file not shown.
|
After Width: | Height: | Size: 4.2 KiB |
@@ -0,0 +1 @@
|
||||
P0;0;0q"1;1;128;128#0;2;0;0;0#1;2;26;35;45#2;2;27;35;45#3;2;27;35;45#4;2;27;35;45#5;2;27;35;45#6;2;27;35;45#7;2;27;35;45#8;2;27;35;46#9;2;27;36;46#10;2;27;35;45#11;2;27;36;45#12;2;27;36;46#13;2;28;36;46#14;2;27;36;47#15;2;28;36;46#16;2;28;36;47#17;2;28;36;47#18;2;27;36;47#19;2;27;36;48#20;2;27;37;48#21;2;28;37;49#22;2;28;36;47#23;2;29;37;47#24;2;29;37;48#25;2;29;37;48#26;2;29;38;48#27;2;29;38;49#28;2;29;38;49#29;2;29;38;49#30;2;29;38;50#31;2;30;38;49#32;2;30;38;49#33;2;30;38;50#34;2;30;39;50#35;2;29;38;51#36;2;29;39;51#37;2;30;39;50#38;2;30;39;51#39;2;30;39;51#40;2;31;39;51#41;2;31;40;51#42;2;31;40;51#43;2;31;40;52#44;2;30;40;53#45;2;29;40;53#46;2;31;41;55#47;2;31;40;51#48;2;31;40;52#49;2;31;41;53#50;2;32;41;53#51;2;32;41;53#52;2;32;41;53#53;2;32;41;53#54;2;32;42;53#55;2;32;42;54#56;2;33;42;54#57;2;32;42;54#58;2;33;42;54#59;2;32;42;55#60;2;33;42;54#61;2;32;42;54#62;2;33;42;54#63;2;32;42;55#64;2;33;42;55#65;2;33;42;54#66;2;33;42;54#67;2;33;42;54#68;2;33;42;55#69;2;32;42;54#70;2;33;42;54#71;2;32;42;54#72;2;33;42;54#73;2;33;42;55#74;2;33;42;54#75;2;33;43;56#76;2;32;43;57#77;2;33;43;56#78;2;33;44;58#79;2;33;44;59#80;2;35;45;58#81;2;33;45;61#82;2;34;45;61#83;2;36;47;61#84;2;36;47;63#85;2;36;48;63#86;2;36;49;65#87;2;38;49;65#88;2;37;51;67#89;2;36;51;72#90;2;36;52;73#91;2;36;53;74#92;2;36;53;74#93;2;36;53;75#94;2;36;53;75#95;2;36;53;75#96;2;36;53;75#97;2;36;53;75#98;2;37;53;75#99;2;36;53;75#100;2;36;53;75#101;2;36;53;75#102;2;37;53;75#103;2;37;53;75#104;2;37;54;75#105;2;39;51;67#106;2;39;51;68#107;2;40;52;70#108;2;40;53;71#109;2;42;54;69#110;2;41;53;70#111;2;42;54;71#112;2;39;53;73#113;2;39;53;72#114;2;40;55;73#115;2;38;54;74#116;2;38;54;74#117;2;38;54;75#118;2;38;54;75#119;2;39;54;75#120;2;40;54;74#121;2;39;54;74#122;2;41;54;72#123;2;36;53;75#124;2;36;53;75#125;2;36;53;75#126;2;37;53;75#127;2;37;53;75#128;2;36;53;75#129;2;36;53;75#130;2;38;54;75#0!128~-!9~^!8N!11^!70~!11^!8N^!9~$#97!9?_ooo_oo_o!8_#129??_#67!70?_#72___#58!7_o_!6o_$#125!13?O??O!9?__#67!83?O-#0!9~}o#101_#125!22?@#101??@!5?C#58!38?_w{!6}!26~N@$#125!9?@K#0!28?!4@BF^!36~^FB!4@#67@@#0!26?o}!9~$#97!10?B^!22~}~~}~!4}ww_-#0!11~}o!33?B^!32~^B!33?o}!11~$#125!11?@?_#129!33?_#58!32?_{!33~N@$#97!12?F^!32~{$#129!12?G-#0!14~w!33?@N!28~NB!33?w!14~$#97!14?Bn!32~}_#58!28?o{!33~F$#125!14?CO#129!33?O-#0!16~w#125O#0!32?@N!24~N@#74!32?_#0w!16~$#97!16?Fn!32~{O#58!24?ow!32~^B$#101!50?A#125_#67!25?C#72!33?C$#73!77?A-#0!18~{_!32?@N!20~N@!32?_{!18~$#97!18?B^!32~}_#58!20?o}!32~^B$#125!53?O-#0!20~{_!32?@F!16~N@!32?_{!20~$#97!20?BN!32~{o#58!16?o}!32~VB$#101!21?O#129!32?A#125G#67!50?G-#0!22~}o#129!32?@#0F!12~F@!32?o}!22~$#97!22?@N!32~}o#58!12?w}!32~N@$#101!57?G-#0!24~}o#125_!31?A#0F!8~F!33?o}!24~$#101!24?@#97N^!31~|w#58!8?o!33~N@$#72!68?G-#0!26~}w#125!32?@?_#58???o}!32~F#72@$#97!26?@F!32~}w#64!4?G#72@#0!32?w}!26~$!61?F^~~~F-!29~w!33?BF#58FN^!29~nB$#97!29?F!33~w#41G?_#72!30?OC$#90!63?C#77O#52GO_#0!30?w!29~$#109!64?_#23_$#37!65?O-#0!31~{_#119!31?@#58A??BN!25~^B$#97!31?B^!31~}o#31@?_#0!26?_{!31~$#7!65?@C#41AO$#109!65?C#23AO#52Co$#130!65?G#1G#48@G$#75!66?O#4_$#111!66?_#28G$#37!67?C-#0!33~{_#101!31?@#7@G#31C_#0!22?_{!33~$#97!33?BN!31~}w#4C_#58@N!21~^B$#125!34?O#80!32?A#17AO#52Eo$#122!67?C#28@#41AO$#83!68?O#48@G$#114!68?_#26G-#0!35~{o#120!32?C_#31C_#0!18?o{!35~$#101!35?@#97F!32~w#4C_#58BN!17~FB$#125!35?A#129G#19!32?@#23@G#52Co#72!17?G$#85!69?A#16A#41AO$#21!70?G#44@G$#87!70?O#7O-#0!37~}o#119!32?C_#31C_#0!14?o}!37~$#125!37?@G#36!32?@#44G@G$#97!38?F!32~w#7Ao#58@N!13~N@$#105!71?A#23@G#52Eo$#4!72?C#41A#37O$#106!72?O-#0!39~}w#46!32?@#7EO#58@N!9~F#67@$#125!39?@#97F!32~w_#28C_#0!10?w}!39~$#101!73?C#23@G#48C_$#107!73?A#76G#37AO$#108!74?O#19_#52AO$#44!75?@#41G-#0!42~w#101o#79!31?@#23@G#48C_#0!5?w!42~$#97!42?FN!31~{_#41@G#58F!5~F$#114!75?A#4A#7O#52BW$#19!76?C#21_#26_$#82!76?G#28C#31O$#112!76?O#37A-#0!44~w_#119!31?AO#31AO#58F^B$#97!44?F^!31~{_#41@G#0?_w!44~$#84!77?@#4A#7O#48C_#72?C$#17!78?@#16G#52BW$#36!78?C#26C#28_$#86!78?G#45_-#0!46~{w!33o{!46~$#97!46?@FF!30NK#23@#44@$#125!46?A#89?G#88!30?@#28A#47A$#101!79?A#78C$#81!80?G-#0!128~-!128B-\
|
||||
@@ -0,0 +1,4 @@
|
||||
<svg width="500" height="500" viewBox="0 0 500 500" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#536b8a" d="M 323.509,471.167 480.202,32.185 C 482.857,24.74 478.671,19.312 470.853,20.071 L 347.629,32.03 c -7.816,0.759 -16.259,7.44 -18.841,14.911 L 183.753,466.465 c -2.583,7.47 1.693,13.535 9.546,13.535 h 119.938 c 3.925,0 8.192,-3.02 9.52,-6.744 l 0.746,-2.089 v 0 0 z"/>
|
||||
<path fill="#5d87be" d="M 30.055,20.07 152.165,32.029 c 7.745,0.76 16.122,7.438 18.692,14.905 l 146.766,426.302 c 1.284,3.735 -0.83,6.764 -4.721,6.764 H 193.294 c -7.781,0 -16.239,-6.044 -18.871,-13.489 L 20.791,32.185 c -2.633,-7.445 1.518,-12.874 9.264,-12.114 v 0 0 z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 671 B |
@@ -0,0 +1,4 @@
|
||||
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
|
||||
<path fill="#536b8a" d="m10.01,14.308l4.189,-12.212c0.071,-0.207 -0.041,-0.358 -0.25,-0.337l-3.295,0.333c-0.209,0.021 -0.435,0.207 -0.504,0.415l-3.878,11.670c-0.069,0.208 0.045,0.376 0.255,0.376l3.207,0c0.105,0 0.219,-0.084 0.255,-0.188l0.02,-0.058l0,0l0,0z"/>
|
||||
<path fill="#5d87be" d="m2.267,1.759l3.247,0.333c0.206,0.021 0.428,0.207 0.497,0.415l3.903,11.859c0.034,0.104 -0.022,0.188 -0.126,0.188l-3.18,0c-0.207,0 -0.431,-0.168 -0.502,-0.375l-4.086,-12.082c-0.07,-0.207 0.04,-0.358 0.246,-0.337l0,0l0,0z"/>
|
||||
</svg>
|
||||
|
After Width: | Height: | Size: 610 B |
@@ -0,0 +1,3 @@
|
||||
asteroids
|
||||
asteroids.js
|
||||
asteroids.wasm
|
||||
@@ -0,0 +1,20 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<meta content="text/html; charset=utf-8" http-equiv="Content-Type">
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>V Asteroids</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>V Asteroids demo</h1>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1" width="800" height="600"></canvas>
|
||||
<script type="text/javascript">
|
||||
var canvasElement = document.getElementById('canvas');
|
||||
var Module = {
|
||||
print(...args) { console.log(...args); },
|
||||
};
|
||||
</script>
|
||||
<script src="asteroids.js"></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,386 @@
|
||||
// Copyright (c) 2025 Delyan Angelov. All rights reserved.
|
||||
// The use of this source code is governed by an MIT license that
|
||||
// can be found in the LICENSE file.
|
||||
module main
|
||||
|
||||
import gg
|
||||
import math
|
||||
import rand
|
||||
import os.asset
|
||||
import math.vec
|
||||
|
||||
type V2 = vec.Vec2[f32]
|
||||
|
||||
fn rads(degrees f32) f32 {
|
||||
return f32(math.radians(degrees))
|
||||
}
|
||||
|
||||
fn (a V2) offset(angle f32, scale f32) V2 {
|
||||
return a + V2{math.cosf(angle) * scale, math.sinf(angle) * scale}
|
||||
}
|
||||
|
||||
fn (mut a V2) wrap(b V2) {
|
||||
a.x = f32(math.mod(a.x + b.x, b.x))
|
||||
a.y = f32(math.mod(a.y + b.y, b.y))
|
||||
}
|
||||
|
||||
fn V2.random(b V2) V2 {
|
||||
return V2{rand.f32() * b.x, rand.f32() * b.y}
|
||||
}
|
||||
|
||||
struct Body {
|
||||
mut:
|
||||
pos V2
|
||||
vel V2
|
||||
rotation f32
|
||||
radius f32
|
||||
active bool = true
|
||||
}
|
||||
|
||||
struct Bullet {
|
||||
Body
|
||||
}
|
||||
|
||||
struct Asteroid {
|
||||
Body
|
||||
mut:
|
||||
segments int
|
||||
offsets []f32
|
||||
points []f32
|
||||
rotation_step f32 = 1.0
|
||||
}
|
||||
|
||||
struct Player {
|
||||
Body
|
||||
mut:
|
||||
is_engine_on bool
|
||||
bullets int = 99
|
||||
bullets_limit int = 99
|
||||
fuel int = 9999
|
||||
fuel_limit int = 9999
|
||||
cooldown int
|
||||
cooldown_frames int = 15
|
||||
points []f32 = []f32{len: 8}
|
||||
}
|
||||
|
||||
struct Game {
|
||||
mut:
|
||||
gg &gg.Context = unsafe { nil }
|
||||
screen V2 = V2{800, 600}
|
||||
player Player
|
||||
bullets []Bullet
|
||||
asteroids []Asteroid
|
||||
score int
|
||||
highscore int
|
||||
ships int = 5
|
||||
level int = 1
|
||||
is_up bool
|
||||
|
||||
msg Message
|
||||
}
|
||||
|
||||
@[params]
|
||||
struct Message {
|
||||
mut:
|
||||
frames int
|
||||
text string
|
||||
size int = 40
|
||||
color gg.Color
|
||||
align gg.HorizontalAlign = .center
|
||||
}
|
||||
|
||||
fn (mut a Asteroid) setup() {
|
||||
a.segments = int(a.radius / 10) * 10
|
||||
a.offsets = []f32{len: a.segments}
|
||||
a.points = []f32{len: 2 * a.segments}
|
||||
for i in 0 .. a.segments {
|
||||
a.offsets[i] = a.radius + 25 * (0.5 - rand.f32())
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut p Player) reset(screen V2) {
|
||||
p.bullets, p.fuel = p.bullets_limit, p.fuel_limit
|
||||
p.pos, p.vel = screen.div_scalar(2), V2{0, 0}
|
||||
p.radius, p.rotation = 15, -90
|
||||
p.active = true
|
||||
}
|
||||
|
||||
fn (mut game Game) handle_input() {
|
||||
mut p := &game.player
|
||||
p.is_engine_on = false
|
||||
if game.gg.is_key_down(.escape) {
|
||||
exit(0)
|
||||
}
|
||||
if !p.active {
|
||||
return
|
||||
}
|
||||
game.is_up = game.gg.is_key_down(.up) || game.gg.is_key_down(.w)
|
||||
is_fire := game.gg.is_key_down(.space)
|
||||
is_left := game.gg.is_key_down(.left) || game.gg.is_key_down(.a)
|
||||
is_right := game.gg.is_key_down(.right) || game.gg.is_key_down(.d)
|
||||
if is_fire && p.cooldown <= 0 && p.active {
|
||||
game.add_bullet()
|
||||
p.cooldown = p.cooldown_frames
|
||||
}
|
||||
if p.fuel >= 10 {
|
||||
if game.is_up && p.active {
|
||||
angle := rads(p.rotation)
|
||||
p.vel += V2{0, 0}.offset(angle, 0.1)
|
||||
p.fuel -= 10
|
||||
p.is_engine_on = true
|
||||
}
|
||||
}
|
||||
if p.fuel >= 1 {
|
||||
if is_left {
|
||||
p.rotation -= 2
|
||||
p.fuel--
|
||||
}
|
||||
if is_right {
|
||||
p.rotation += 2
|
||||
p.fuel--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut game Game) update() {
|
||||
game.msg.frames = int_max(0, game.msg.frames - 1)
|
||||
mut p := &game.player
|
||||
p.cooldown = int_max(0, p.cooldown - 1)
|
||||
p.pos += p.vel
|
||||
p.pos.wrap(game.screen)
|
||||
for mut b in game.bullets {
|
||||
if !b.active {
|
||||
continue
|
||||
}
|
||||
b.pos += b.vel
|
||||
if b.pos.x < 0 || b.pos.x > game.screen.x || b.pos.y < 0 || b.pos.y > game.screen.y {
|
||||
b.active = false
|
||||
}
|
||||
}
|
||||
for mut a in game.asteroids {
|
||||
if !a.active {
|
||||
continue
|
||||
}
|
||||
a.pos += a.vel
|
||||
a.pos.wrap(game.screen)
|
||||
a.rotation += a.rotation_step
|
||||
if p.active && p.pos.distance(a.pos) <= (p.radius + a.radius) {
|
||||
// player/asteroids collision
|
||||
p.active = false
|
||||
a.active = false
|
||||
game.split_asteroid(a, p.vel.mul_scalar(0.5))
|
||||
game.player.reset(game.screen)
|
||||
game.score += 50
|
||||
game.show_message(text: 'Your ship was destroyed.', color: gg.red, frames: 90)
|
||||
game.ships--
|
||||
if game.ships <= 0 {
|
||||
game.ships = 5
|
||||
game.score = 0
|
||||
game.asteroids = []
|
||||
game.add_asteroids(10)
|
||||
}
|
||||
}
|
||||
}
|
||||
for mut b in game.bullets {
|
||||
if !b.active {
|
||||
continue
|
||||
}
|
||||
for mut a in game.asteroids {
|
||||
if !a.active {
|
||||
continue
|
||||
}
|
||||
if b.active && b.pos.distance(a.pos) <= (b.radius + a.radius) {
|
||||
// bullet/asteroid collision
|
||||
b.active = false
|
||||
a.active = false
|
||||
game.score += 100
|
||||
game.split_asteroid(a, b.vel.mul_scalar(0.2))
|
||||
}
|
||||
}
|
||||
}
|
||||
if game.bullets.any(!it.active) {
|
||||
game.bullets = game.bullets.filter(it.active)
|
||||
}
|
||||
if game.asteroids.any(!it.active) {
|
||||
game.asteroids = game.asteroids.filter(it.active)
|
||||
}
|
||||
if game.asteroids.len == 0 {
|
||||
game.level++
|
||||
game.ships++
|
||||
game.player.reset(game.screen)
|
||||
game.add_asteroids(10)
|
||||
game.show_message(text: 'YOU WIN', color: gg.green, frames: 90)
|
||||
}
|
||||
game.highscore = int_max(game.score, game.highscore)
|
||||
}
|
||||
|
||||
fn (mut game Game) show_message(params Message) {
|
||||
game.msg = params
|
||||
}
|
||||
|
||||
fn (mut game Game) split_asteroid(a &Asteroid, vel V2) {
|
||||
if a.radius < 30 {
|
||||
return
|
||||
}
|
||||
shrink_factor := 0.5 + 0.3 * rand.f32()
|
||||
mut a1 := Asteroid{
|
||||
...*a
|
||||
active: true
|
||||
radius: a.radius * shrink_factor
|
||||
rotation_step: -a.rotation_step
|
||||
}
|
||||
mut a2 := Asteroid{
|
||||
...*a
|
||||
active: true
|
||||
radius: a.radius * (1 - shrink_factor)
|
||||
rotation_step: 2 * a.rotation_step
|
||||
}
|
||||
a1.vel = a1.vel.mul_scalar(shrink_factor) * vel
|
||||
a2.vel = a2.vel.mul_scalar(1 - shrink_factor) * (V2{0, 0} - vel)
|
||||
a1.setup()
|
||||
a2.setup()
|
||||
game.asteroids << a1
|
||||
game.asteroids << a2
|
||||
}
|
||||
|
||||
fn (mut game Game) draw() {
|
||||
ws := gg.window_size()
|
||||
game.screen = V2{ws.width, ws.height}
|
||||
game.gg.draw_rect_filled(0, 0, game.screen.x, game.screen.y, gg.rgba(20, 20, 20, 255))
|
||||
game.draw_ship()
|
||||
for b in game.bullets {
|
||||
game.gg.draw_circle_filled(b.pos.x, b.pos.y, 3, gg.yellow)
|
||||
}
|
||||
for mut a in game.asteroids {
|
||||
game.draw_asteroid(mut a)
|
||||
}
|
||||
scenter := game.screen.div_scalar(2)
|
||||
label1 := 'Level: ${game.level} Ships: ${game.ships}'
|
||||
game.gg.draw_text(5, 10, label1, size: 20, color: gg.white, align: .left)
|
||||
label2 := 'B: ${game.player.bullets:02} F: ${game.player.fuel:04}'
|
||||
game.gg.draw_text(int(scenter.x), 10, label2, size: 20, color: gg.green, align: .center)
|
||||
label3 := 'Score: ${game.score} Highscore: ${game.highscore}'
|
||||
game.gg.draw_text(int(game.screen.x) - 5, 10, label3, size: 20, color: gg.white, align: .right)
|
||||
if game.msg.frames > 0 {
|
||||
game.gg.draw_text(int(scenter.x), int(scenter.y / 2), game.msg.text,
|
||||
size: game.msg.size
|
||||
color: game.msg.color
|
||||
align: game.msg.align
|
||||
)
|
||||
}
|
||||
label4 := 'Use arrows + space to control your ship. Use Escape to end the game.'
|
||||
game.gg.draw_text(int(game.screen.x - 5), int(game.screen.y - 20), label4,
|
||||
size: 16
|
||||
color: gg.gray
|
||||
align: .right
|
||||
)
|
||||
}
|
||||
|
||||
fn (game &Game) draw_ship() {
|
||||
mut p := &game.player
|
||||
if !p.active {
|
||||
return
|
||||
}
|
||||
angle := rads(p.rotation)
|
||||
p1 := p.pos.offset(angle, p.radius)
|
||||
p2 := p.pos.offset(angle + 2.5, 0.6 * p.radius)
|
||||
p3 := p.pos.offset(angle, -0.3 * p.radius)
|
||||
p4 := p.pos.offset(angle - 2.5, 0.6 * p.radius)
|
||||
p.points[0] = p1.x
|
||||
p.points[1] = p1.y
|
||||
p.points[2] = p2.x
|
||||
p.points[3] = p2.y
|
||||
p.points[4] = p3.x
|
||||
p.points[5] = p3.y
|
||||
p.points[6] = p4.x
|
||||
p.points[7] = p4.y
|
||||
game.gg.draw_convex_poly(p.points, gg.white)
|
||||
if p.is_engine_on {
|
||||
engine := p.pos.offset(angle + math.pi, 0.7 * p.radius)
|
||||
game.gg.draw_circle_filled(engine.x, engine.y, 6, gg.yellow)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut game Game) draw_asteroid(mut a Asteroid) {
|
||||
if !a.active {
|
||||
return
|
||||
}
|
||||
game.gg.draw_circle_filled(a.pos.x, a.pos.y, a.radius, gg.rgba(235, 235, 255, 215))
|
||||
for i in 0 .. a.segments {
|
||||
angle := rads(a.rotation + f32(i) * 360 / a.segments)
|
||||
p := a.pos.offset(angle, a.offsets[i])
|
||||
a.points[i * 2], a.points[i * 2 + 1] = p.x, p.y
|
||||
}
|
||||
game.gg.draw_convex_poly(a.points, gg.rgba(155, 155, 148, 245))
|
||||
}
|
||||
|
||||
fn (mut game Game) add_bullet() {
|
||||
if game.player.bullets <= 0 {
|
||||
return
|
||||
}
|
||||
game.player.bullets--
|
||||
angle := rads(game.player.rotation)
|
||||
game.bullets << Bullet{
|
||||
pos: game.player.pos
|
||||
radius: 3
|
||||
vel: game.player.vel.offset(angle, 10)
|
||||
active: true
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut game Game) add_asteroids(count int) {
|
||||
for _ in 0 .. count {
|
||||
mut npos := V2{}
|
||||
new_asteroid_loop: for {
|
||||
npos = V2.random(game.screen)
|
||||
for a in game.asteroids {
|
||||
if a.pos.distance(npos) < (a.radius + 30) {
|
||||
continue new_asteroid_loop
|
||||
}
|
||||
}
|
||||
if game.player.pos.distance(npos) < 5 * (game.player.radius + 30) {
|
||||
continue new_asteroid_loop
|
||||
}
|
||||
break
|
||||
}
|
||||
radius := 50 + 50 * (0.5 - rand.f32())
|
||||
mut asteroid := Asteroid{
|
||||
pos: npos
|
||||
vel: (V2.random(game.screen) - V2.random(game.screen)) / game.screen
|
||||
radius: radius
|
||||
rotation: 360 * rand.f32()
|
||||
rotation_step: 2 * (0.5 - rand.f32())
|
||||
active: true
|
||||
}
|
||||
asteroid.setup()
|
||||
game.asteroids << asteroid
|
||||
}
|
||||
}
|
||||
|
||||
fn on_frame(mut game Game) {
|
||||
if game.gg.timer.elapsed().milliseconds() > 15 {
|
||||
game.gg.timer.restart()
|
||||
game.handle_input()
|
||||
game.update()
|
||||
}
|
||||
game.gg.begin()
|
||||
game.draw()
|
||||
game.gg.end()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut game := &Game{}
|
||||
mut fpath := asset.get_path('../assets', 'fonts/RobotoMono-Regular.ttf')
|
||||
game.player.reset(game.screen)
|
||||
game.add_asteroids(10)
|
||||
game.gg = gg.new_context(
|
||||
window_title: 'V Asteroids'
|
||||
width: int(game.screen.x)
|
||||
height: int(game.screen.y)
|
||||
frame_fn: on_frame
|
||||
user_data: game
|
||||
sample_count: 2
|
||||
font_path: fpath
|
||||
)
|
||||
game.gg.run()
|
||||
}
|
||||
@@ -0,0 +1,146 @@
|
||||
// Binary Search Tree example by @SleepyRoy
|
||||
|
||||
struct Empty {}
|
||||
|
||||
struct Node[T] {
|
||||
value T
|
||||
left Tree[T]
|
||||
right Tree[T]
|
||||
}
|
||||
|
||||
type Tree[T] = Empty | Node[T]
|
||||
|
||||
// return size(number of nodes) of BST
|
||||
fn (tree Tree[T]) size[T]() int {
|
||||
return match tree {
|
||||
Empty { 0 }
|
||||
Node[T] { 1 + tree.left.size() + tree.right.size() }
|
||||
}
|
||||
}
|
||||
|
||||
// insert a value to BST
|
||||
fn (tree Tree[T]) insert[T](x T) Tree[T] {
|
||||
return match tree {
|
||||
Empty {
|
||||
Node[T]{x, tree, tree}
|
||||
}
|
||||
Node[T] {
|
||||
if x == tree.value {
|
||||
tree
|
||||
} else if x < tree.value {
|
||||
Node[T]{
|
||||
...tree
|
||||
left: tree.left.insert(x)
|
||||
}
|
||||
} else {
|
||||
Node[T]{
|
||||
...tree
|
||||
right: tree.right.insert(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// whether able to find a value in BST
|
||||
fn (tree Tree[T]) search[T](x T) bool {
|
||||
return match tree {
|
||||
Empty {
|
||||
false
|
||||
}
|
||||
Node[T] {
|
||||
if x == tree.value {
|
||||
true
|
||||
} else if x < tree.value {
|
||||
tree.left.search(x)
|
||||
} else {
|
||||
tree.right.search(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// find the minimal value of a BST
|
||||
fn (tree Tree[T]) min[T]() T {
|
||||
return match tree {
|
||||
Empty {
|
||||
T(1e9)
|
||||
}
|
||||
Node[T] {
|
||||
if tree.value < tree.left.min() {
|
||||
tree.value
|
||||
} else {
|
||||
tree.left.min()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// delete a value in BST (if nonexistent do nothing)
|
||||
fn (tree Tree[T]) delete[T](x T) Tree[T] {
|
||||
return match tree {
|
||||
Empty {
|
||||
tree
|
||||
}
|
||||
Node[T] {
|
||||
if tree.left !is Empty && tree.right !is Empty {
|
||||
if x < tree.value {
|
||||
Node[T]{
|
||||
...tree
|
||||
left: tree.left.delete(x)
|
||||
}
|
||||
} else if x > tree.value {
|
||||
Node[T]{
|
||||
...tree
|
||||
right: tree.right.delete(x)
|
||||
}
|
||||
} else {
|
||||
Node[T]{
|
||||
...tree
|
||||
value: tree.right.min()
|
||||
right: tree.right.delete(tree.right.min())
|
||||
}
|
||||
}
|
||||
} else if tree.left !is Empty {
|
||||
if x == tree.value {
|
||||
tree.left
|
||||
} else {
|
||||
Node[T]{
|
||||
...tree
|
||||
left: tree.left.delete(x)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if x == tree.value {
|
||||
tree.right
|
||||
} else {
|
||||
Node[T]{
|
||||
...tree
|
||||
right: tree.right.delete(x)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut tree := Tree[f64](Empty{})
|
||||
vals := [0.2, 0.0, 0.5, 0.3, 0.6, 0.8, 0.9, 1.0, 0.1, 0.4, 0.7]
|
||||
for i in vals {
|
||||
tree = tree.insert(i)
|
||||
}
|
||||
println('[1] after insertion tree size is ${tree.size()}') // 11
|
||||
del_vals := [-0.3, 0.0, 0.3, 0.6, 1.0, 1.5]
|
||||
for i in del_vals {
|
||||
tree = tree.delete(i)
|
||||
}
|
||||
print('[2] after deletion tree size is ${tree.size()}, ') // 7
|
||||
print('and these elements were deleted: ') // 0.0 0.3 0.6 1.0
|
||||
for i in vals {
|
||||
if !tree.search(i) {
|
||||
print('${i} ')
|
||||
}
|
||||
}
|
||||
println('')
|
||||
}
|
||||
@@ -0,0 +1,165 @@
|
||||
import os
|
||||
import term
|
||||
|
||||
// For a more detailed description of the brainfuck language, see:
|
||||
// https://en.wikipedia.org/wiki/Brainfuck
|
||||
// http://brainfuck.org/brainfuck.html ,
|
||||
// http://brainfuck.org/epistle.html ,
|
||||
// http://www.hevanet.com/cristofd/brainfuck/ .
|
||||
|
||||
const show_state = os.getenv('VERBOSE') != ''
|
||||
|
||||
struct BFState {
|
||||
mut:
|
||||
pc u16 // program counter (PC) register
|
||||
address u16 // a 16-bit address register, serving as an index to the memory below
|
||||
program string // the BF program
|
||||
memory []u8 = []u8{len: 65536} // we have 2^16 bytes of memory
|
||||
targets map[int]int // a mapping for the program address of a `[` to its corresponding `]`, and from a `]` to its corresponding opening `[`.
|
||||
}
|
||||
|
||||
fn BFState.new(program string) &BFState {
|
||||
mut state := &BFState{
|
||||
program: program
|
||||
}
|
||||
state.find_matching_pairs()
|
||||
return state
|
||||
}
|
||||
|
||||
// show the current state of an BF interpreter. Useful for debugging.
|
||||
fn (state &BFState) show(suffix string) {
|
||||
mut max_non_zero_address := -1
|
||||
for i := state.memory.len - 1; i >= 0; i-- {
|
||||
if state.memory[i] != 0 {
|
||||
max_non_zero_address = i
|
||||
break
|
||||
}
|
||||
}
|
||||
println('PC: ${state.pc:04} | Address: ${state.address:04} | Memory: ${state.memory#[0..
|
||||
max_non_zero_address + 1]:-40s} | Memory[Address]: ${state.memory#[state.address..
|
||||
state.address + 1]:-10s} | ${suffix}')
|
||||
}
|
||||
|
||||
// find_matching_pairs fills in the `targets` mapping for all pairs of `[` and `]`,
|
||||
// so that when interpreting, we would not have to search for them anymore.
|
||||
fn (mut state BFState) find_matching_pairs() {
|
||||
mut stack := []int{}
|
||||
for i in 0 .. state.program.len {
|
||||
pi := state.program[i]
|
||||
match pi {
|
||||
`[` {
|
||||
stack << i
|
||||
}
|
||||
`]` {
|
||||
if stack.len == 0 {
|
||||
eprintln('> unmatched `]` found in the program, at position: ${i}')
|
||||
eprintln('program so far:')
|
||||
eprintln(state.program#[0..i + 1])
|
||||
exit(1)
|
||||
}
|
||||
pc := stack.pop()
|
||||
state.targets[pc] = i + 1
|
||||
state.targets[i] = pc + 1
|
||||
// eprintln('>>> found `[` at i ${i}; pc: ${pc}')
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
if stack.len > 0 {
|
||||
eprintln('> found ${stack.len} unmatched `[`:')
|
||||
for i in stack {
|
||||
eprintln(' `[` at position: ${i}, program so far: `${state.program#[0..i + 1]}`')
|
||||
}
|
||||
exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@[noreturn]
|
||||
fn (state &BFState) panic_for_bracket(b1 rune, b2 rune) {
|
||||
panic('unbalanced `${b1}` found, its target `${b2}` is not known; address: ${state.address}, pc: ${state.pc}')
|
||||
}
|
||||
|
||||
fn (mut state BFState) run() ? {
|
||||
mut i := 0
|
||||
// the BF interpreter starts here:
|
||||
for state.pc < state.program.len {
|
||||
// get the current program character (corresponding to our program counter), and interpret it according to BF's rules:
|
||||
instruction := state.program[state.pc]
|
||||
$if trace_execution ? {
|
||||
state.show('instruction ${i:08}: ${instruction:02x}, ${rune(instruction)}')
|
||||
}
|
||||
match instruction {
|
||||
`>` {
|
||||
state.address++ // increment the address
|
||||
}
|
||||
`<` {
|
||||
state.address-- // decrement the address
|
||||
}
|
||||
`+` {
|
||||
state.memory[state.address]++ // increment the value at the address
|
||||
}
|
||||
`-` {
|
||||
state.memory[state.address]-- // decrement the value at the address
|
||||
}
|
||||
`.` {
|
||||
print(rune(state.memory[state.address])) // print the value at the address
|
||||
flush_stdout() // ensure that even single characters are printed immediately, and not buffered
|
||||
}
|
||||
`,` {
|
||||
inp := u8(term.utf8_getchar() or { 0 }) // read a character value from the standard input/terminal
|
||||
state.memory[state.address] = inp
|
||||
}
|
||||
`[` {
|
||||
if state.memory[state.address] == 0 {
|
||||
state.pc = u16(state.targets[state.pc])
|
||||
continue
|
||||
}
|
||||
}
|
||||
`]` {
|
||||
if state.memory[state.address] != 0 {
|
||||
state.pc = u16(state.targets[state.pc])
|
||||
continue
|
||||
}
|
||||
}
|
||||
`#` {
|
||||
state.show('#')
|
||||
}
|
||||
else {
|
||||
// The interpreter should ignore characters that are not part of the language.
|
||||
// I.e. they are treated like programmer comments.
|
||||
}
|
||||
}
|
||||
|
||||
i++
|
||||
// increment the program counter to go to the next instruction
|
||||
state.pc++
|
||||
// go back to the line `for state.pc < state.program.len {`
|
||||
}
|
||||
}
|
||||
|
||||
@[noreturn]
|
||||
fn show_usage() {
|
||||
eprintln('you need to supply a brainfuck program/expression as a string argument,')
|
||||
eprintln('or filename.b, if it is located in a file (note the `.b` or `.bf` extension).')
|
||||
exit(1) // exit with non-zero exit code if there is no program to run
|
||||
}
|
||||
|
||||
fn main() {
|
||||
if os.args.len < 2 {
|
||||
show_usage()
|
||||
}
|
||||
mut program := os.args[1] // our program is fed in as a string
|
||||
if program.ends_with('.b') || program.ends_with('.bf') {
|
||||
program = os.read_file(program) or {
|
||||
eprintln('error reading file ${program}: ${err}')
|
||||
show_usage()
|
||||
}
|
||||
}
|
||||
|
||||
mut state := BFState.new(program)
|
||||
state.run()
|
||||
|
||||
if show_state {
|
||||
state.show('FINAL')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
breakout
|
||||
breakout.js
|
||||
breakout.wasm
|
||||
@@ -0,0 +1,20 @@
|
||||
<!doctype html>
|
||||
<html lang="en-us">
|
||||
<head>
|
||||
<meta charset=utf-8>
|
||||
<meta content="text/html; charset=utf-8" http-equiv=Content-Type>
|
||||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||||
<title>V Breakout</title>
|
||||
</head>
|
||||
<body>
|
||||
<h1>V Breakout demo</h1>
|
||||
<canvas id="canvas" oncontextmenu="event.preventDefault()" tabindex="-1" width="600" height="800"></canvas>
|
||||
<script type='text/javascript'>
|
||||
var canvasElement = document.getElementById('canvas');
|
||||
var Module = {
|
||||
print(...args) { console.log(...args); },
|
||||
};
|
||||
</script>
|
||||
<script src=breakout.js></script>
|
||||
</body>
|
||||
</html>
|
||||
@@ -0,0 +1,331 @@
|
||||
// vtest build: !openbsd
|
||||
import gg
|
||||
import math
|
||||
import rand
|
||||
import sokol.audio
|
||||
import os.asset
|
||||
import sokol.sgl
|
||||
|
||||
const designed_width = 600
|
||||
const designed_height = 800
|
||||
const brick_width = 53
|
||||
const brick_height = 20
|
||||
const bevel_size = int(brick_height * 0.18)
|
||||
const highlight_color = gg.rgba(255, 255, 255, 65)
|
||||
const shade_color = gg.rgba(0, 0, 0, 65)
|
||||
|
||||
struct Brick {
|
||||
mut:
|
||||
x f32
|
||||
y f32
|
||||
w f32 = brick_width
|
||||
h f32 = brick_height
|
||||
c gg.Color
|
||||
value int
|
||||
alive bool = true
|
||||
}
|
||||
|
||||
struct Game {
|
||||
mut:
|
||||
width int = designed_width
|
||||
height int = designed_height
|
||||
ball_x f32
|
||||
ball_y f32
|
||||
ball_r f32 = 10.0
|
||||
ball_dx f32 = 4
|
||||
ball_dy f32 = -4
|
||||
paddle_x f32 = 250
|
||||
paddle_w f32 = 100
|
||||
paddle_h f32 = 20
|
||||
paddle_dx f32 = 8
|
||||
bricks []Brick
|
||||
nbricks int
|
||||
npaddles int = 10
|
||||
npoints int
|
||||
nlevels int = 1
|
||||
nevent int
|
||||
sound SoundManager
|
||||
ctx &gg.Context = unsafe { nil }
|
||||
}
|
||||
|
||||
fn Game.new() &Game {
|
||||
mut g := &Game{}
|
||||
g.ball_x, g.ball_y = f32(g.width) / 2, f32(g.height) - g.paddle_h
|
||||
g.init_bricks()
|
||||
return g
|
||||
}
|
||||
|
||||
enum SoundKind {
|
||||
paddle
|
||||
brick
|
||||
wall
|
||||
lose_ball
|
||||
}
|
||||
|
||||
struct SoundManager {
|
||||
mut:
|
||||
sounds [4][]f32 // TODO: using map[SoundKind][]f32 here breaks emscripten; use map after the fix
|
||||
initialised bool
|
||||
}
|
||||
|
||||
fn (mut sm SoundManager) init() {
|
||||
all_kinds := [SoundKind.paddle, .brick, .wall, .lose_ball]!
|
||||
sample_rate := f32(audio.sample_rate())
|
||||
duration, volume := 0.09, f32(.25)
|
||||
nframes := int(sample_rate * duration)
|
||||
for i in 0 .. nframes {
|
||||
t := f32(i) / sample_rate
|
||||
sm.sounds[int(SoundKind.paddle)] << volume * math.sinf(t * 936.0 * 2 * math.pi)
|
||||
sm.sounds[int(SoundKind.brick)] << volume * math.sinf(t * 432.0 * 2 * math.pi)
|
||||
sm.sounds[int(SoundKind.wall)] << volume * math.sinf(t * 174.0 * 2 * math.pi)
|
||||
sm.sounds[int(SoundKind.lose_ball)] << math.sinf(t * 123.0 * 2 * math.pi)
|
||||
}
|
||||
border_samples := 2000
|
||||
for k, s := f32(0), 0; s <= border_samples; k, s = k + 1.0 / f32(border_samples), s + 1 {
|
||||
rk := f32(1) - k
|
||||
rs := nframes - border_samples - 1 + s
|
||||
for kind in all_kinds {
|
||||
sm.sounds[int(kind)][s] *= k
|
||||
sm.sounds[int(kind)][rs] *= rk
|
||||
}
|
||||
}
|
||||
sm.initialised = true
|
||||
}
|
||||
|
||||
fn (mut g Game) play(k SoundKind) {
|
||||
if g.sound.initialised {
|
||||
s := g.sound.sounds[int(k)]
|
||||
audio.push(s.data, s.len)
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Game) init_bricks() {
|
||||
yoffset, xoffset := f32(50 + rand.intn(100) or { 0 }), f32(0 + rand.intn(50) or { 0 })
|
||||
g.bricks.clear()
|
||||
g.nbricks = 0
|
||||
for row in 0 .. 10 {
|
||||
for col in 0 .. 10 {
|
||||
g.bricks << Brick{
|
||||
x: col * (brick_width + 1) + xoffset
|
||||
y: row * (brick_height + 1) + yoffset
|
||||
c: gg.rgb(0x40 | rand.u8(), 0x40 | rand.u8(), 0x40 | rand.u8())
|
||||
value: 10 - row
|
||||
}
|
||||
g.nbricks++
|
||||
}
|
||||
}
|
||||
for _ in 0 .. 5 + rand.intn(10) or { 0 } {
|
||||
i := rand.intn(g.bricks.len - 1) or { 0 }
|
||||
if g.bricks[i].alive {
|
||||
g.bricks[i].alive = false
|
||||
g.nbricks--
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Game) draw() {
|
||||
ws := gg.window_size()
|
||||
g.ctx.begin()
|
||||
sgl.push_matrix()
|
||||
sgl.scale(f32(ws.width) / f32(designed_width), f32(ws.height) / f32(designed_height), 0)
|
||||
|
||||
g.draw_paddle()
|
||||
g.draw_ball()
|
||||
for brick in g.bricks {
|
||||
if brick.alive {
|
||||
g.draw_brick(brick)
|
||||
}
|
||||
}
|
||||
label1 := 'Level: ${g.nlevels:02} Points: ${g.npoints:06}'
|
||||
label2 := 'Bricks: ${g.nbricks:03} Paddles: ${g.npaddles:02}'
|
||||
g.ctx.draw_text(5, 3, label1, size: 24, color: gg.rgb(255, 255, 255))
|
||||
g.ctx.draw_text(320, 3, label2, size: 24, color: gg.rgb(255, 255, 255))
|
||||
|
||||
sgl.pop_matrix()
|
||||
g.ctx.end()
|
||||
}
|
||||
|
||||
fn (g &Game) draw_ball() {
|
||||
g.ctx.draw_circle_filled(g.ball_x, g.ball_y, g.ball_r, gg.red)
|
||||
mut ball_r_less := g.ball_r
|
||||
for _ in 0 .. 3 {
|
||||
ball_r_less *= 0.8
|
||||
g.ctx.draw_circle_filled(g.ball_x - g.ball_r + ball_r_less, g.ball_y - g.ball_r +
|
||||
ball_r_less, ball_r_less, highlight_color)
|
||||
}
|
||||
}
|
||||
|
||||
fn (g &Game) draw_paddle() {
|
||||
roffset, rradius := -5, 18
|
||||
g.ctx.draw_circle_filled(g.paddle_x - roffset, g.height, rradius, gg.blue)
|
||||
g.ctx.draw_circle_filled(g.paddle_x + g.paddle_w + roffset, g.height, rradius, gg.blue)
|
||||
g.ctx.draw_rect_filled(g.paddle_x, g.height - g.paddle_h + 2, g.paddle_w, g.paddle_h, gg.blue)
|
||||
g.ctx.draw_rect_filled(g.paddle_x, g.height - g.paddle_h + 2, g.paddle_w, bevel_size,
|
||||
highlight_color)
|
||||
}
|
||||
|
||||
fn (g &Game) draw_brick(brick Brick) {
|
||||
g.ctx.draw_rect_filled(brick.x, brick.y, brick.w, brick.h, brick.c)
|
||||
g.ctx.draw_rect_filled(brick.x, brick.y, brick.w, bevel_size, highlight_color)
|
||||
g.ctx.draw_rect_filled(brick.x, brick.y, bevel_size, brick.h - bevel_size, highlight_color)
|
||||
g.ctx.draw_rect_filled(brick.x + brick.w - bevel_size, brick.y, bevel_size,
|
||||
brick.h - bevel_size, shade_color)
|
||||
g.ctx.draw_rect_filled(brick.x, brick.y + brick.h - bevel_size, brick.w, bevel_size,
|
||||
shade_color)
|
||||
}
|
||||
|
||||
fn (mut g Game) game_over() {
|
||||
g.init_bricks()
|
||||
g.npoints, g.nlevels, g.npaddles = 0, 1, 5
|
||||
}
|
||||
|
||||
fn (mut g Game) goto_next_level() {
|
||||
g.init_bricks()
|
||||
g.npaddles++
|
||||
g.nlevels++
|
||||
}
|
||||
|
||||
fn (mut g Game) move(k f32) {
|
||||
if k < 0 {
|
||||
if g.paddle_x <= 0 {
|
||||
return
|
||||
}
|
||||
} else if k > 0 {
|
||||
if g.paddle_x >= g.width - g.paddle_w {
|
||||
return
|
||||
}
|
||||
}
|
||||
g.paddle_x += k * g.paddle_dx
|
||||
}
|
||||
|
||||
fn (mut g Game) update() {
|
||||
if g.ctx.pressed_keys[gg.KeyCode.left] {
|
||||
g.move(-1.0)
|
||||
}
|
||||
if g.ctx.pressed_keys[gg.KeyCode.right] {
|
||||
g.move(1.0)
|
||||
}
|
||||
//
|
||||
g.ball_x, g.ball_y = g.ball_x + g.ball_dx, g.ball_y + g.ball_dy
|
||||
// Wall collisions
|
||||
if g.ball_x < g.ball_r || g.ball_x > g.width - g.ball_r {
|
||||
g.ball_dx *= -1
|
||||
g.play(.wall)
|
||||
}
|
||||
if g.ball_y < g.ball_r {
|
||||
g.ball_dy *= -1
|
||||
g.play(.wall)
|
||||
}
|
||||
if g.ball_y > g.height {
|
||||
g.ball_x, g.ball_y = g.paddle_x + g.paddle_w / 2, f32(g.height) - g.paddle_h
|
||||
g.ball_dy = -4
|
||||
g.npaddles--
|
||||
g.play(.lose_ball)
|
||||
if g.npaddles <= 0 {
|
||||
g.game_over()
|
||||
}
|
||||
}
|
||||
// Paddle collision
|
||||
is_ball_on_paddle_y := g.ball_y + g.ball_r > g.height - g.paddle_h
|
||||
&& g.ball_y < g.height - g.ball_r
|
||||
is_ball_on_paddle_x := g.ball_x > g.paddle_x - 10 && g.ball_x < g.paddle_x + g.paddle_w + 10
|
||||
if is_ball_on_paddle_y && is_ball_on_paddle_x {
|
||||
g.play(.paddle)
|
||||
g.ball_dy = -math.abs(g.ball_dy)
|
||||
x_in_paddle := g.ball_x - g.paddle_x
|
||||
rmargin := 10
|
||||
if x_in_paddle < rmargin || x_in_paddle + rmargin > g.paddle_w {
|
||||
g.ball_dx *= -1
|
||||
} else if !(x_in_paddle > 40 && x_in_paddle < 60) {
|
||||
r := 10 * (-0.5 + rand.f32())
|
||||
g.ball_dx += r
|
||||
g.ball_dx = f32(int_min(int_max(-80, int(g.ball_dx * 10)), 80)) / 10
|
||||
}
|
||||
}
|
||||
// Brick collisions
|
||||
for mut brick in g.bricks {
|
||||
if brick.alive && g.ball_y - g.ball_r < brick.y + brick.h && g.ball_y + g.ball_r > brick.y
|
||||
&& g.ball_x + g.ball_r > brick.x && g.ball_x - g.ball_r < brick.x + brick.w {
|
||||
g.play(.brick)
|
||||
brick.alive = false
|
||||
g.nbricks--
|
||||
g.npoints += brick.value
|
||||
g.ball_dy *= -1
|
||||
if g.nbricks == 0 {
|
||||
g.goto_next_level()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut g Game) touch_event(touch_point gg.TouchPoint) {
|
||||
ws := gg.window_size()
|
||||
tx := touch_point.pos_x
|
||||
if tx <= f32(ws.width) * 0.5 {
|
||||
g.move(-1.0)
|
||||
} else {
|
||||
g.move(1.0)
|
||||
}
|
||||
}
|
||||
|
||||
@[if wasm32_emscripten]
|
||||
fn (mut g Game) handle_event() {
|
||||
if g.nevent > 0 {
|
||||
return
|
||||
}
|
||||
// the audio has to be started when the wasm canvas has received user
|
||||
// interaction, unlike on desktop platforms
|
||||
audio.setup(buffer_frames: 1024)
|
||||
g.sound.init()
|
||||
g.nevent++
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut g := Game.new()
|
||||
mut fpath := asset.get_path('../assets', 'fonts/RobotoMono-Regular.ttf')
|
||||
$if !wasm32_emscripten {
|
||||
audio.setup(buffer_frames: 512) // too small values lead to cracking sounds or no sound at all on macos
|
||||
g.sound.init()
|
||||
fpath = ''
|
||||
}
|
||||
g.ctx = gg.new_context(
|
||||
width: g.width
|
||||
height: g.height
|
||||
window_title: 'V Breakout'
|
||||
frame_fn: fn (mut g Game) {
|
||||
dt := g.ctx.timer.elapsed().milliseconds()
|
||||
if dt > 15 {
|
||||
g.update()
|
||||
g.ctx.timer.restart()
|
||||
}
|
||||
g.draw()
|
||||
}
|
||||
click_fn: fn (x f32, y f32, btn gg.MouseButton, mut g Game) {
|
||||
g.handle_event()
|
||||
}
|
||||
event_fn: fn (e &gg.Event, mut g Game) {
|
||||
g.handle_event()
|
||||
if e.typ == .touches_began || e.typ == .touches_moved {
|
||||
if e.num_touches > 0 {
|
||||
touch_point := e.touches[0]
|
||||
g.touch_event(touch_point)
|
||||
}
|
||||
}
|
||||
}
|
||||
keydown_fn: fn (key gg.KeyCode, _ gg.Modifier, mut g Game) {
|
||||
g.handle_event()
|
||||
match key {
|
||||
.r {
|
||||
g.game_over()
|
||||
}
|
||||
.escape {
|
||||
exit(0)
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
user_data: g
|
||||
font_path: fpath
|
||||
)
|
||||
g.ctx.run()
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
import datatypes
|
||||
|
||||
struct KeyVal {
|
||||
mut:
|
||||
key int
|
||||
val int
|
||||
}
|
||||
|
||||
fn (a KeyVal) == (b KeyVal) bool {
|
||||
return a.key == b.key
|
||||
}
|
||||
|
||||
fn (a KeyVal) < (b KeyVal) bool {
|
||||
return a.key < b.key
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut bst := datatypes.BSTree[KeyVal]{}
|
||||
bst.insert(KeyVal{ key: 1, val: 12 })
|
||||
println(bst.in_order_traversal())
|
||||
|
||||
bst.insert(KeyVal{ key: 2, val: 34 })
|
||||
bst.insert(KeyVal{ key: -2, val: 203 })
|
||||
|
||||
for elem in bst.in_order_traversal() {
|
||||
println(elem.val)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
// Simple raw HTTP head request
|
||||
import net
|
||||
import time
|
||||
import io
|
||||
|
||||
fn main() {
|
||||
// Make a new connection
|
||||
mut conn := net.dial_tcp('google.com:80')!
|
||||
// Simple http HEAD request for a file
|
||||
conn.write_string('GET /index.html HTTP/1.0\r\n\r\n')!
|
||||
// Wrap in a buffered reader
|
||||
mut r := io.new_buffered_reader(reader: conn)
|
||||
for {
|
||||
l := r.read_line() or { break }
|
||||
println('${l}')
|
||||
// Make it nice and obvious that we are doing this line by line
|
||||
time.sleep(100 * time.millisecond)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
# Files and directories made by build.vsh:
|
||||
/target/
|
||||
/test.txt
|
||||
|
||||
# Pre-compiled build.vsh
|
||||
/build
|
||||
@@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env -S v run
|
||||
|
||||
import build
|
||||
import time
|
||||
|
||||
// Define variables that can be used to change tasks in the buildscript
|
||||
const app_name = 'hello'
|
||||
const program_args = 'World'
|
||||
const build_dir = 'target'
|
||||
|
||||
// Make the build context
|
||||
mut context := build.context(
|
||||
// Set the default task to `release` when no arguments are provided
|
||||
default: 'release'
|
||||
)
|
||||
|
||||
// Add a few simple tasks
|
||||
context.task(name: 'doc', run: |self| system('echo "Nothing to do"'))
|
||||
context.task(name: 'run', run: |self| system('v run . ${program_args}'))
|
||||
context.task(name: 'build', run: |self| system('v .'))
|
||||
context.task(name: 'build.prod', run: |self| system('v -prod -o ${app_name} .'))
|
||||
|
||||
// `_` to denote "private" tasks. Nothing stops the user from using it, but
|
||||
// this tells them that the task is not meant to be used by them.
|
||||
context.task(
|
||||
name: '_mkdirs'
|
||||
// The `help` field is displayed in `--tasks` to give a short summary of what the task does.
|
||||
help: 'Makes the directories used by the application'
|
||||
run: fn (self build.Task) ! {
|
||||
if !exists(build_dir) {
|
||||
mkdir_all(build_dir) or { panic(err) }
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
// This task will only run when the `test.txt` file is outdated
|
||||
context.artifact(
|
||||
name: 'test.txt'
|
||||
help: 'Generate test.txt'
|
||||
run: fn (self build.Task) ! {
|
||||
write_file('test.txt', time.now().str())!
|
||||
}
|
||||
)
|
||||
|
||||
// Add a more complex task
|
||||
context.task(
|
||||
name: 'release'
|
||||
help: 'Build the app in production mode, generates documentation, and releases the build on Git'
|
||||
depends: ['_mkdirs', 'doc', 'test.txt']
|
||||
run: fn (self build.Task) ! {
|
||||
system('v -prod -o ${build_dir}/${app_name} .')
|
||||
// Pretend we are using Git to publish the built file as a release here.
|
||||
}
|
||||
)
|
||||
|
||||
// Run the build context. This will iterate over os.args and each corresponding
|
||||
// task, skipping any arguments that start with a hyphen (-)
|
||||
context.run()
|
||||
@@ -0,0 +1,5 @@
|
||||
import os
|
||||
|
||||
fn main() {
|
||||
println('Hello, ${os.args[1]}!')
|
||||
}
|
||||
@@ -0,0 +1,98 @@
|
||||
import os
|
||||
|
||||
// Example of C interop for a very handy task.
|
||||
//
|
||||
// wkhtmltopdf and wkhtmltoimage are open source (LGPLv3) command line tools to
|
||||
// render HTML into PDF and various image formats using the Qt WebKit rendering
|
||||
// engine. These run entirely "headless" and do not require a display or display
|
||||
// service.
|
||||
//
|
||||
// https://github.com/wkhtmltopdf/wkhtmltopdf
|
||||
// https://wkhtmltopdf.org/downloads.html
|
||||
// https://wkhtmltopdf.org/libwkhtmltox/
|
||||
#flag -lwkhtmltox
|
||||
#include "wkhtmltox/pdf.h" # You can install the C package for your system from the wkhtmltopdf.org/downloads.html page
|
||||
|
||||
pub struct C.wkhtmltopdf_global_settings {}
|
||||
|
||||
pub struct C.wkhtmltopdf_object_settings {}
|
||||
|
||||
pub struct C.wkhtmltopdf_converter {}
|
||||
|
||||
fn C.wkhtmltopdf_init(use_graphics bool) i32
|
||||
|
||||
fn C.wkhtmltopdf_deinit() i32
|
||||
|
||||
fn C.wkhtmltopdf_version() &char
|
||||
|
||||
fn C.wkhtmltopdf_create_global_settings() &C.wkhtmltopdf_global_settings
|
||||
|
||||
fn C.wkhtmltopdf_destroy_global_settings(global_settings &C.wkhtmltopdf_global_settings)
|
||||
|
||||
fn C.wkhtmltopdf_set_global_setting(global_settings &C.wkhtmltopdf_global_settings, name &char, value &char) bool
|
||||
|
||||
fn C.wkhtmltopdf_create_object_settings() &C.wkhtmltopdf_object_settings
|
||||
|
||||
fn C.wkhtmltopdf_destroy_object_settings(object_settings &C.wkhtmltopdf_object_settings)
|
||||
|
||||
fn C.wkhtmltopdf_set_object_setting(object_settings &C.wkhtmltopdf_object_settings, name &char, value &char) bool
|
||||
|
||||
fn C.wkhtmltopdf_create_converter(global_settings &C.wkhtmltopdf_global_settings) &C.wkhtmltopdf_converter
|
||||
|
||||
fn C.wkhtmltopdf_destroy_converter(converter &C.wkhtmltopdf_converter)
|
||||
|
||||
fn C.wkhtmltopdf_add_object(converter &C.wkhtmltopdf_converter, object_settings &C.wkhtmltopdf_object_settings,
|
||||
data &char)
|
||||
|
||||
fn C.wkhtmltopdf_convert(converter &C.wkhtmltopdf_converter) bool
|
||||
|
||||
fn C.wkhtmltopdf_http_error_code(converter &C.wkhtmltopdf_converter) i32
|
||||
|
||||
fn C.wkhtmltopdf_get_output(converter &C.wkhtmltopdf_converter, data &&char) i32
|
||||
|
||||
fn main() {
|
||||
// init
|
||||
init := C.wkhtmltopdf_init(0)
|
||||
println('wkhtmltopdf_init: ${init}')
|
||||
version := unsafe { cstring_to_vstring(&char(C.wkhtmltopdf_version())) }
|
||||
println('wkhtmltopdf_version: ${version}')
|
||||
global_settings := C.wkhtmltopdf_create_global_settings()
|
||||
println('wkhtmltopdf_create_global_settings: ${voidptr(global_settings)}')
|
||||
object_settings := C.wkhtmltopdf_create_object_settings()
|
||||
println('wkhtmltopdf_create_object_settings')
|
||||
converter := C.wkhtmltopdf_create_converter(global_settings)
|
||||
println('wkhtmltopdf_create_converter: ${voidptr(converter)}')
|
||||
// convert
|
||||
mut result := C.wkhtmltopdf_set_object_setting(object_settings, c'page',
|
||||
c'http://www.google.com.br')
|
||||
println('wkhtmltopdf_set_object_setting: ${result} [page = http://www.google.com.br]')
|
||||
C.wkhtmltopdf_add_object(converter, object_settings, 0)
|
||||
println('wkhtmltopdf_add_object')
|
||||
result = C.wkhtmltopdf_convert(converter)
|
||||
println('wkhtmltopdf_convert: ${result}')
|
||||
error_code := C.wkhtmltopdf_http_error_code(converter)
|
||||
println('wkhtmltopdf_http_error_code: ${error_code}')
|
||||
if result {
|
||||
pdata := &char(unsafe { nil })
|
||||
ppdata := &pdata
|
||||
nbytes := C.wkhtmltopdf_get_output(converter, voidptr(ppdata))
|
||||
println('wkhtmltopdf_get_output: ${nbytes} bytes')
|
||||
mut file := os.open_file('./google.pdf', 'w+', 0o666) or {
|
||||
println('ERR: ${err}')
|
||||
return
|
||||
}
|
||||
wrote := unsafe { file.write_ptr(pdata, nbytes) }
|
||||
println('write_bytes: ${wrote} [./google.pdf]')
|
||||
file.flush()
|
||||
file.close()
|
||||
}
|
||||
// destroy
|
||||
C.wkhtmltopdf_destroy_converter(converter)
|
||||
println('wkhtmltopdf_destroy_converter')
|
||||
C.wkhtmltopdf_destroy_object_settings(object_settings)
|
||||
println('wkhtmltopdf_destroy_object_settings: ${voidptr(object_settings)}')
|
||||
C.wkhtmltopdf_destroy_global_settings(global_settings)
|
||||
println('wkhtmltopdf_destroy_global_settings')
|
||||
deinit := C.wkhtmltopdf_deinit()
|
||||
println('wkhtmltopdf_deinit: ${deinit}')
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
#include <stdio.h>
|
||||
|
||||
fn C.puts(&char) i32
|
||||
|
||||
fn main() {
|
||||
C.puts(c'V can call C functions like `puts` too.')
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
# Ignore the generated C files in this directory
|
||||
*.c
|
||||
@@ -0,0 +1,54 @@
|
||||
## A simple example to show how to call a function written in v from c
|
||||
|
||||
### Compile as a shared library
|
||||
|
||||
#### On Linux:
|
||||
|
||||
Step 1: Compile the v code to a shared library using `v -cc gcc -shared v_test_print.v` or
|
||||
`v -cc gcc -shared v_test_math.v`.
|
||||
|
||||
Step 2: Compile the c file using `gcc test_print.c v_test_print.so -o test_print -Wl,-rpath=.` or
|
||||
`gcc test_math.c v_test_math.so -o test_math -Wl,-rpath=.`.
|
||||
|
||||
Step 3: Run the compiled c file using `./test_print` or `./test_math`.
|
||||
|
||||
|
||||
#### On Mac OSX:
|
||||
|
||||
On Mac OSX, libgc can be obtained from homebrew by `brew install libgc`.
|
||||
During compiling and/or linking, `-I/usr/local/include -L/usr/local/lib` (for x86_64),
|
||||
or `-I/opt/homebrew/include -L/opt/homebrew/lib` (for arm64) can be added depending the arch.
|
||||
|
||||
Step 1: Compile the v code to a shared library using `v -cc gcc -shared v_test_print.v` or
|
||||
`v -cc gcc -shared v_test_math.v`.
|
||||
|
||||
Step 2: Compile the c file using `gcc test_print.c v_test_print.dylib -o test_print` or
|
||||
`gcc test_math.c v_test_math.dylib -o test_math`.
|
||||
|
||||
Step 3: Run the compiled c file using `LD_LIBRARY_PATH=. ./test_print` or
|
||||
`LD_LIBRARY_PATH=. ./test_math`.
|
||||
|
||||
#### On Windows:
|
||||
|
||||
Step 1: Compile the v code to a shared library using `v -cc gcc -shared v_test_print.v` or
|
||||
`v -cc gcc -shared v_test_math.v`.
|
||||
|
||||
Step 2: Compile the c file using `gcc test_print.c v_test_print.dll -o test_print.exe` or
|
||||
`gcc test_math.c v_test_math.dll -o test_math.exe`.
|
||||
|
||||
Step 3: Run the compiled c file using `test_print.exe` or `test_math.exe`.
|
||||
|
||||
### Compile as a c file
|
||||
|
||||
***Requirements: `libgc` must be installed***
|
||||
|
||||
Step 1: Compile the v code to a shared library using
|
||||
`v -shared -cc gcc -o v_test_print.c v_test_print.v` or
|
||||
`v -shared -cc gcc -o v_test_math.c v_test_math.v`.
|
||||
|
||||
*Specifying the output with a `.c` extension will generate the corresponding C source file.*
|
||||
|
||||
Step 2: Compile the c file using `gcc test_print.c v_test_print.c -o test_print -lgc` or
|
||||
`gcc test_math.c v_test_math.c -o test_math -lgc -lm`.
|
||||
|
||||
Step 3: Run the compiled c file using `./test_print` or `./test_math`.
|
||||
@@ -0,0 +1,14 @@
|
||||
// vtest build: false // Check the README.md for detailed information; this file needs special compilation options
|
||||
module test_math
|
||||
|
||||
import math
|
||||
|
||||
@[export: 'square']
|
||||
fn calculate_square(i int) int {
|
||||
return i * i
|
||||
}
|
||||
|
||||
@[export: 'sqrt_of_sum_of_squares']
|
||||
fn calculate_sqrt_of_sum_of_squares(x f64, y f64) f64 {
|
||||
return math.sqrt(x * x + y * y)
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
// vtest build: false // Check the README.md for detailed information; this file needs special compilation options
|
||||
module test_print
|
||||
|
||||
@[export: 'foo']
|
||||
fn show_foo(s &char) {
|
||||
println(unsafe { cstring_to_vstring(s) })
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
A simple example to show how to call a function written in v from python
|
||||
|
||||
Step 1: Compile the v code to a shared library using `v -d no_backtrace -shared test.v`
|
||||
|
||||
Step 2: Run the python file using `python3 test.py`
|
||||
|
||||
Note: you do not need `-d no_backtrace` if you use gcc or clang .
|
||||
@@ -0,0 +1,26 @@
|
||||
from ctypes import *
|
||||
import math, os
|
||||
|
||||
## Load the V shared library:
|
||||
so_file="./test.so"
|
||||
if os.name=="nt":
|
||||
so_file="./test.dll"
|
||||
lib = CDLL(so_file)
|
||||
|
||||
## Pass an integer to a V function, and receiving back an integer:
|
||||
print("lib.square(10) result is", lib.square(10))
|
||||
assert lib.square(10) == 100, "Cannot validate V square()."
|
||||
|
||||
## Pass a floating point number to a V function:
|
||||
lib.sqrt_of_sum_of_squares.restype = c_double
|
||||
assert lib.sqrt_of_sum_of_squares(c_double(1.1), c_double(2.2)) == math.sqrt(1.1*1.1 + 2.2*2.2), "Cannot validate V sqrt_of_sum_of_squares()."
|
||||
|
||||
## Passing a V string to a V function, and receiving back a V string:
|
||||
class VString(Structure):
|
||||
_fields_ = [("str", c_char_p), ("len", c_int)]
|
||||
|
||||
lib.process_v_string.argtypes = [VString]
|
||||
lib.process_v_string.restype = VString
|
||||
|
||||
assert lib.process_v_string(VString(b'World', 5)).str == b'v World v'
|
||||
print('Hello', str(lib.process_v_string(VString(b'World', 5)).str, 'utf-8'))
|
||||
@@ -0,0 +1,21 @@
|
||||
// vtest build: present_python? // the example only makes sense to be compiled, when python is installed
|
||||
module test
|
||||
|
||||
// Note: compile this with `v -d no_backtrace -shared test.v`
|
||||
import math
|
||||
|
||||
@[export: 'square']
|
||||
fn square(i int) int {
|
||||
return i * i
|
||||
}
|
||||
|
||||
@[export: 'sqrt_of_sum_of_squares']
|
||||
fn sqrt_of_sum_of_squares(x f64, y f64) f64 {
|
||||
return math.sqrt(x * x + y * y)
|
||||
}
|
||||
|
||||
// you do not have to use the same name in the export attribute
|
||||
@[export: 'process_v_string']
|
||||
fn work(s string) string {
|
||||
return 'v ${s} v'
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
A simple example to show how to call a function written in v from ruby
|
||||
|
||||
Step 1: Compile the v code to a shared library using `v -d no_backtrace -shared test.v`
|
||||
|
||||
Step 2: Run the ruby file using `ruby test.rb`
|
||||
|
||||
Note: you do not need `-d no_backtrace` if you use gcc or clang .
|
||||
@@ -0,0 +1,39 @@
|
||||
require 'bundler/inline'
|
||||
|
||||
gemfile do
|
||||
source 'https://rubygems.org'
|
||||
gem 'ffi'
|
||||
end
|
||||
|
||||
require 'ffi'
|
||||
|
||||
# extension for shared libraries varies by platform - see vlib/dl/dl.v
|
||||
# get_shared_library_extension()
|
||||
def shared_library_extension
|
||||
if Gem.win_platform?
|
||||
'.dll'
|
||||
elsif RUBY_PLATFORM =~ /darwin/ # MacOS
|
||||
'.dylib'
|
||||
else
|
||||
'.so'
|
||||
end
|
||||
end
|
||||
|
||||
module Lib
|
||||
extend FFI::Library
|
||||
|
||||
begin
|
||||
ffi_lib File.join(File.dirname(__FILE__), 'test' + shared_library_extension)
|
||||
rescue LoadError
|
||||
abort("No shared library test#{shared_library_extension} found. Check examples/call_v_from_ruby/README.md")
|
||||
end
|
||||
|
||||
attach_function :square, [:int], :int
|
||||
attach_function :sqrt_of_sum_of_squares, [:double, :double], :double
|
||||
end
|
||||
|
||||
puts "Lib.square(10) result is #{Lib.square(10)}"
|
||||
raise 'Cannot validate V square().' unless Lib.square(10) == 100
|
||||
|
||||
raise 'Cannot validate V sqrt_of_sum_of_squares().' unless \
|
||||
Lib.sqrt_of_sum_of_squares(1.1, 2.2) == Math.sqrt(1.1*1.1 + 2.2*2.2)
|
||||
@@ -0,0 +1,14 @@
|
||||
// vtest build: present_ruby? // the example only makes sense to be compiled, when ruby is installed
|
||||
module test
|
||||
|
||||
import math
|
||||
|
||||
@[export: 'square']
|
||||
fn square(i int) int {
|
||||
return i * i
|
||||
}
|
||||
|
||||
@[export: 'sqrt_of_sum_of_squares']
|
||||
fn sqrt_of_sum_of_squares(x f64, y f64) f64 {
|
||||
return math.sqrt(x * x + y * y)
|
||||
}
|
||||
@@ -0,0 +1,78 @@
|
||||
module main
|
||||
|
||||
import encoding.cbor
|
||||
import encoding.hex
|
||||
import time
|
||||
|
||||
struct Address {
|
||||
street string
|
||||
city string
|
||||
zip string @[cbor: 'postal_code']
|
||||
}
|
||||
|
||||
struct User {
|
||||
name string
|
||||
age u32
|
||||
email ?string
|
||||
tags []string
|
||||
address Address
|
||||
signed_up time.Time
|
||||
internal string @[skip]
|
||||
}
|
||||
|
||||
fn main() {
|
||||
user := User{
|
||||
name: 'Alice'
|
||||
age: 30
|
||||
email: 'alice@example.com'
|
||||
tags: ['admin', 'beta']
|
||||
address: Address{
|
||||
street: '1 Test Lane'
|
||||
city: 'Paris'
|
||||
zip: '75000'
|
||||
}
|
||||
signed_up: time.parse_iso8601('2025-01-15T10:00:00Z') or { time.now() }
|
||||
internal: 'will not be encoded'
|
||||
}
|
||||
|
||||
// 1. Generic typed encode/decode
|
||||
bytes := cbor.encode[User](user, cbor.EncodeOpts{})!
|
||||
println('encoded ${bytes.len} bytes: ${hex.encode(bytes)}')
|
||||
|
||||
back := cbor.decode[User](bytes, cbor.DecodeOpts{})!
|
||||
println('round-trip name=${back.name} age=${back.age} city=${back.address.city}')
|
||||
|
||||
// 2. Canonical encoding for stable hashing/signing
|
||||
mut m := map[string]int{}
|
||||
m['z'] = 26
|
||||
m['a'] = 1
|
||||
m['m'] = 13
|
||||
canonical := cbor.encode[map[string]int](m, cbor.EncodeOpts{
|
||||
canonical: true
|
||||
})!
|
||||
println('canonical map: ${hex.encode(canonical)}')
|
||||
|
||||
// 3. Decode an unknown payload into a Value tree
|
||||
v := cbor.decode[cbor.Value](bytes, cbor.DecodeOpts{})!
|
||||
if name_val := v.get('name') {
|
||||
if s := name_val.as_string() {
|
||||
println('peeked name from Value tree: ${s}')
|
||||
}
|
||||
}
|
||||
|
||||
// 4. Manual streaming: build a CBOR array of mixed types
|
||||
mut p := cbor.new_packer(cbor.EncodeOpts{})
|
||||
p.pack_array_header(3)
|
||||
p.pack_uint(42)
|
||||
p.pack_text('hello')
|
||||
p.pack_bool(true)
|
||||
stream_bytes := p.bytes()
|
||||
println('manual stream: ${hex.encode(stream_bytes)}')
|
||||
|
||||
mut up := cbor.new_unpacker(stream_bytes, cbor.DecodeOpts{})
|
||||
n := up.unpack_array_header()!
|
||||
first := up.unpack_uint()!
|
||||
second := up.unpack_text()!
|
||||
third := up.unpack_bool()!
|
||||
println('unpacked array of ${n}: ${first}, "${second}", ${third}')
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
module main
|
||||
|
||||
import cli { Command, Flag }
|
||||
import os
|
||||
|
||||
fn main() {
|
||||
mut cmd := Command{
|
||||
name: 'cli'
|
||||
description: 'An example of the cli library.'
|
||||
version: '1.0.0'
|
||||
}
|
||||
mut greet_cmd := Command{
|
||||
name: 'greet'
|
||||
description: 'Prints greeting in different languages.'
|
||||
usage: '<name>'
|
||||
required_args: 1
|
||||
pre_execute: greet_pre_func
|
||||
execute: greet_func
|
||||
post_execute: greet_post_func
|
||||
}
|
||||
greet_cmd.add_flag(Flag{
|
||||
flag: .string
|
||||
required: true
|
||||
name: 'language'
|
||||
abbrev: 'l'
|
||||
description: 'Language of the message.'
|
||||
})
|
||||
greet_cmd.add_flag(Flag{
|
||||
flag: .int
|
||||
name: 'times'
|
||||
default_value: ['3']
|
||||
description: 'Number of times the message gets printed.'
|
||||
})
|
||||
greet_cmd.add_flag(Flag{
|
||||
flag: .string_array
|
||||
name: 'fun'
|
||||
description: 'Just a dumby flags to show multiple.'
|
||||
})
|
||||
cmd.add_command(greet_cmd)
|
||||
cmd.setup()
|
||||
cmd.parse(os.args)
|
||||
}
|
||||
|
||||
fn greet_func(cmd Command) ! {
|
||||
language := cmd.flags.get_string('language') or {
|
||||
panic('Failed to get `language` flag: ${err}')
|
||||
}
|
||||
times := cmd.flags.get_int('times') or { panic('Failed to get `times` flag: ${err}') }
|
||||
name := cmd.args[0]
|
||||
for _ in 0 .. times {
|
||||
match language {
|
||||
'english', 'en' {
|
||||
println('Welcome ${name}')
|
||||
}
|
||||
'german', 'de' {
|
||||
println('Willkommen ${name}')
|
||||
}
|
||||
'dutch', 'nl' {
|
||||
println('Welkom ${name}')
|
||||
}
|
||||
else {
|
||||
println('Unsupported language')
|
||||
println('Supported languages are `english`, `german` and `dutch`.')
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
fun := cmd.flags.get_strings('fun') or { panic('Failed to get `fun` flag: ${err}') }
|
||||
for f in fun {
|
||||
println('fun: ${f}')
|
||||
}
|
||||
}
|
||||
|
||||
fn greet_pre_func(_cmd Command) ! {
|
||||
println('This is a function running before the main function.\n')
|
||||
}
|
||||
|
||||
fn greet_post_func(_cmd Command) ! {
|
||||
println('\nThis is a function running after the main function.')
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
// This example demonstrates the `vlib/cli` features that show up automatically
|
||||
// in `--help` when you populate the optional `group`, `examples` and
|
||||
// `learn_more` fields of `Command`. No opt-in is required — leaving any of
|
||||
// them empty simply skips the corresponding section.
|
||||
//
|
||||
// Compile from the V repo root:
|
||||
// v -o tasky examples/cli_groups.v
|
||||
//
|
||||
// Try it out:
|
||||
// ./tasky --help # root help with grouped commands and examples
|
||||
// ./tasky issue --help # sub-command help with INHERITED FLAGS
|
||||
// ./tasky issue list -v
|
||||
// ./tasky version
|
||||
module main
|
||||
|
||||
import cli
|
||||
import os
|
||||
|
||||
fn main() {
|
||||
mut app := cli.Command{
|
||||
name: 'tasky'
|
||||
description: 'A tiny issue tracker CLI'
|
||||
version: '0.1.0'
|
||||
posix_mode: true
|
||||
examples: [
|
||||
'\$ tasky issue list',
|
||||
'\$ tasky issue create --title "Fix CI"',
|
||||
'\$ tasky config get editor',
|
||||
]
|
||||
learn_more: 'Use `tasky <command> --help` for details about a command.\nDocumentation lives at https://example.test/tasky'
|
||||
}
|
||||
app.add_flag(cli.Flag{
|
||||
flag: .string
|
||||
name: 'config'
|
||||
abbrev: 'c'
|
||||
description: 'Path to a tasky config file'
|
||||
global: true
|
||||
})
|
||||
app.add_command(issue_command())
|
||||
app.add_command(config_command())
|
||||
app.setup()
|
||||
app.parse(os.args)
|
||||
}
|
||||
|
||||
fn issue_command() cli.Command {
|
||||
return cli.Command{
|
||||
name: 'issue'
|
||||
description: 'Work with issues'
|
||||
group: 'Core commands'
|
||||
commands: [
|
||||
cli.Command{
|
||||
name: 'list'
|
||||
description: 'List open issues'
|
||||
execute: issue_list
|
||||
flags: [
|
||||
cli.Flag{
|
||||
flag: .bool
|
||||
name: 'verbose'
|
||||
abbrev: 'v'
|
||||
description: 'Show issue bodies in addition to titles'
|
||||
},
|
||||
]
|
||||
},
|
||||
cli.Command{
|
||||
name: 'create'
|
||||
description: 'Create a new issue'
|
||||
execute: issue_create
|
||||
flags: [
|
||||
cli.Flag{
|
||||
flag: .string
|
||||
name: 'title'
|
||||
abbrev: 't'
|
||||
description: 'Title of the issue to create'
|
||||
required: true
|
||||
},
|
||||
]
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn config_command() cli.Command {
|
||||
return cli.Command{
|
||||
name: 'config'
|
||||
description: 'Read or write tasky settings'
|
||||
group: 'Additional commands'
|
||||
commands: [
|
||||
cli.Command{
|
||||
name: 'get'
|
||||
description: 'Print the value of a config key'
|
||||
usage: '<key>'
|
||||
required_args: 1
|
||||
execute: config_get
|
||||
},
|
||||
cli.Command{
|
||||
name: 'set'
|
||||
description: 'Update a config key'
|
||||
usage: '<key> <value>'
|
||||
required_args: 2
|
||||
execute: config_set
|
||||
},
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
fn issue_list(cmd cli.Command) ! {
|
||||
verbose := cmd.flags.get_bool('verbose') or { false }
|
||||
println('Listing issues (verbose=${verbose})')
|
||||
}
|
||||
|
||||
fn issue_create(cmd cli.Command) ! {
|
||||
title := cmd.flags.get_string('title')!
|
||||
println('Created issue: ${title}')
|
||||
}
|
||||
|
||||
fn config_get(cmd cli.Command) ! {
|
||||
println('config get ${cmd.args[0]}')
|
||||
}
|
||||
|
||||
fn config_set(cmd cli.Command) ! {
|
||||
println('config set ${cmd.args[0]} = ${cmd.args[1]}')
|
||||
}
|
||||
@@ -0,0 +1,158 @@
|
||||
// An X11 clock modeled after https://en.wikipedia.org/wiki/Station_clock
|
||||
// This is a small V example that was based off of the fireworks example.
|
||||
// Written by Stefan Schroeder in 2021 for the v project examples.
|
||||
// See LICENSE for license information.
|
||||
import gg
|
||||
import math
|
||||
import time
|
||||
|
||||
// All coordinates are designed for a clock size of this many pixel.
|
||||
// You cannot change the size of the clock by adjusting this value.
|
||||
const design_size = 700
|
||||
const center = 350
|
||||
|
||||
// Half the width of a tic-mark.
|
||||
const tw = 9
|
||||
// Height of a minute tic-mark. (hour is twice, 3-hour is thrice)
|
||||
const th = 25
|
||||
// Padding of tic-mark to window border
|
||||
const tp = 10
|
||||
|
||||
const tic_color = gg.Color{
|
||||
r: 50
|
||||
g: 50
|
||||
b: 50
|
||||
}
|
||||
const hand_color = gg.black
|
||||
const second_hand_color = gg.red
|
||||
|
||||
struct App {
|
||||
minutes_tic []f32 = [f32(center - tw), tp, center + tw, tp, center + tw, tp, center + tw,
|
||||
tp + 1 * th, center - tw, tp + 1 * th]
|
||||
hours_tic []f32 = [f32(center - tw), tp, center + tw, tp, center + tw, tp, center + tw,
|
||||
tp + 2 * th, center - tw, tp + 2 * th]
|
||||
hours3_tic []f32 = [f32(center - tw), tp, center + tw, tp, center + tw, tp, center + tw,
|
||||
tp + 3 * th, center - tw, tp + 3 * th]
|
||||
|
||||
hour_hand []f32 = [f32(329), 161, 350, 140, 371, 161, 371, 413, 329, 413]
|
||||
minute_hand []f32 = [f32(334.25), 40.25, 350, 24.5, 365.75, 40.25, 365.75, 427, 334.25, 427]
|
||||
second_hand []f32 = [f32(345.8), 38.5, 350, 34.3, 354.2000, 38.5, 358.75, 427, 341.25, 427]
|
||||
mut:
|
||||
gg &gg.Context = unsafe { nil }
|
||||
draw_flag bool = true
|
||||
dpi_scale f32 = 1.0
|
||||
}
|
||||
|
||||
fn on_frame(mut app App) {
|
||||
if !app.draw_flag {
|
||||
return
|
||||
}
|
||||
app.gg.begin()
|
||||
|
||||
for i in 0 .. 60 { // draw minute tics
|
||||
draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.minutes_tic, tic_color, i * 6)
|
||||
}
|
||||
for i in 0 .. 12 { // hours
|
||||
draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.hours_tic, tic_color, i * 30)
|
||||
}
|
||||
for i in 0 .. 4 { // 3 hours
|
||||
draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.hours3_tic, tic_color, i * 90)
|
||||
}
|
||||
|
||||
n := time.now()
|
||||
|
||||
// draw hour hand
|
||||
i := f32(n.hour) + f32(n.minute) / 60.0
|
||||
draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.hour_hand, hand_color, i * 30)
|
||||
|
||||
// draw minute hand
|
||||
mut j := f32(n.minute)
|
||||
if n.second == 59 { // make minute hand move smoothly
|
||||
j += f32(math.sin(f32(n.nanosecond) / 1e9 * math.pi / 2.0))
|
||||
}
|
||||
draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.minute_hand, hand_color, j * 6)
|
||||
|
||||
// draw second hand with smooth transition
|
||||
k := f32(n.second) + f32(math.sin(f32(n.nanosecond) / 1e9 * math.pi / 2.0))
|
||||
draw_convex_poly_rotate(mut app.gg, app.dpi_scale, app.second_hand, second_hand_color,
|
||||
0 + k * 6)
|
||||
|
||||
app.gg.end()
|
||||
}
|
||||
|
||||
// Rotate a polygon round the centerpoint
|
||||
@[manualfree]
|
||||
fn draw_convex_poly_rotate(mut ctx gg.Context, dpi_scale f32, points []f32, c gg.Color, angle f32) {
|
||||
sa := math.sin(math.pi * angle / 180.0)
|
||||
ca := math.cos(math.pi * angle / 180.0)
|
||||
|
||||
mut rotated_points := []f32{cap: points.len}
|
||||
for i := 0; i < points.len / 2; i++ {
|
||||
x := points[2 * i]
|
||||
y := points[2 * i + 1]
|
||||
xn := f32((x - center) * ca - (y - center) * sa)
|
||||
yn := f32((x - center) * sa + (y - center) * ca)
|
||||
rotated_points << (xn + center) * dpi_scale
|
||||
rotated_points << (yn + center) * dpi_scale
|
||||
}
|
||||
ctx.draw_convex_poly(rotated_points, c)
|
||||
unsafe { rotated_points.free() }
|
||||
}
|
||||
|
||||
fn (mut app App) resize() {
|
||||
size := gg.window_size()
|
||||
// avoid calls when minimized
|
||||
if size.width < 2 && size.height < 2 {
|
||||
return
|
||||
}
|
||||
w := f32(size.width) / design_size
|
||||
h := f32(size.height) / design_size
|
||||
app.dpi_scale = if w < h { w } else { h }
|
||||
}
|
||||
|
||||
fn on_event(e &gg.Event, mut app App) {
|
||||
match e.typ {
|
||||
.resized, .resumed {
|
||||
app.resize()
|
||||
}
|
||||
.iconified {
|
||||
app.draw_flag = false
|
||||
}
|
||||
.restored {
|
||||
app.draw_flag = true
|
||||
app.resize()
|
||||
}
|
||||
else {
|
||||
if e.typ == .key_down {
|
||||
match e.key_code {
|
||||
.q {
|
||||
println('Good bye.')
|
||||
// do we need to free anything here?
|
||||
app.gg.quit()
|
||||
}
|
||||
else {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn on_init(mut app App) {
|
||||
app.resize()
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println("Press 'q' to quit.")
|
||||
mut app := &App{}
|
||||
app.gg = gg.new_context(
|
||||
width: design_size
|
||||
height: design_size
|
||||
window_title: 'Clock!'
|
||||
bg_color: gg.white
|
||||
user_data: app
|
||||
frame_fn: on_frame
|
||||
event_fn: on_event
|
||||
init_fn: on_init
|
||||
)
|
||||
app.gg.run()
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
// This example shows how to use V's compile time defines and values via the -d flag and $d() function.
|
||||
//
|
||||
// To change the default values below, pass compile flags to v using: `-d ident=<value>`
|
||||
// Examples:
|
||||
// ´´´
|
||||
// v -d header -d pad=10 -d jobs=8 run d_compile_value.v
|
||||
// v -d id="Bob" -d pad=2 -d pad_char='$' -d jobs=10 run d_compile_value.v
|
||||
// ```
|
||||
|
||||
const pad = $d('pad', 5)
|
||||
const pad_char = $d('pad_char', `-`)
|
||||
|
||||
const footer = '
|
||||
Available compile time flags:
|
||||
-d pad=<i64>
|
||||
-d pad_char=<character>
|
||||
-d id="<string>"
|
||||
-d jobs=<i64>
|
||||
-d header=<bool>
|
||||
You can turn this message off with:
|
||||
-d footer=false'
|
||||
|
||||
struct Job {
|
||||
id string = $d('id', 'Job') // adjust with `-d id="My ID"`
|
||||
}
|
||||
|
||||
struct App {
|
||||
jobs [$d('jobs', 4)]Job // adjust fixed array size with `-d jobs=6`
|
||||
}
|
||||
|
||||
// println_padded prints `str` to stdout with a padding.
|
||||
// Padding length and padding character can be adjusted at compile time
|
||||
// via `-d pad=1` and `-d pad_char=x`.
|
||||
fn println_padded(str string) {
|
||||
for _ in 0 .. pad {
|
||||
print(rune(pad_char))
|
||||
}
|
||||
println(' ${str}')
|
||||
}
|
||||
|
||||
fn main() {
|
||||
header := $d('header', false) // adjust header variable with `-d header=true` or simply `-d header`
|
||||
if header {
|
||||
println_padded('Compile Value Example')
|
||||
}
|
||||
|
||||
app := App{}
|
||||
for i, job in app.jobs {
|
||||
println_padded('${job.id} ${i + 1}')
|
||||
}
|
||||
|
||||
// Turn the footer message off using `-d footer=false`
|
||||
if $d('footer', true) {
|
||||
println(footer)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
struct App {}
|
||||
|
||||
fn (mut app App) method_one() {}
|
||||
|
||||
fn (mut app App) method_two() int {
|
||||
return 0
|
||||
}
|
||||
|
||||
fn (mut app App) method_three(s string) string {
|
||||
return s
|
||||
}
|
||||
|
||||
fn main() {
|
||||
$for method in App.methods {
|
||||
$if method.typ is fn (string) string {
|
||||
println('${method.name} IS `fn(string) string`')
|
||||
} $else {
|
||||
println('${method.name} is NOT `fn(string) string`')
|
||||
}
|
||||
$if method.return_type !is int {
|
||||
println('${method.name} does NOT return `int`')
|
||||
} $else {
|
||||
println('${method.name} DOES return `int`')
|
||||
}
|
||||
$if method.args[0].typ !is string {
|
||||
println("${method.name}'s first arg is NOT `string`")
|
||||
} $else {
|
||||
println("${method.name}'s first arg IS `string`")
|
||||
}
|
||||
// TODO: Double inversion, should this even be allowed?
|
||||
$if method.typ is fn () {
|
||||
println('${method.name} IS a void method')
|
||||
} $else {
|
||||
println('${method.name} is NOT a void method')
|
||||
}
|
||||
println('')
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
// An example deserializer implementation
|
||||
|
||||
struct User {
|
||||
name string
|
||||
age int
|
||||
}
|
||||
|
||||
fn main() {
|
||||
data := 'name=Alice\nage=18'
|
||||
user := decode[User](data)
|
||||
println(user)
|
||||
}
|
||||
|
||||
fn decode[T](data string) T {
|
||||
mut result := T{}
|
||||
// compile-time `for` loop
|
||||
// T.fields gives an array of a field metadata type
|
||||
$for field in T.fields {
|
||||
$if field.typ is string {
|
||||
// $(string_expr) produces an identifier
|
||||
result.$(field.name) = get_string(data, field.name)
|
||||
} $else $if field.typ is int {
|
||||
result.$(field.name) = get_int(data, field.name)
|
||||
}
|
||||
}
|
||||
return result
|
||||
}
|
||||
|
||||
fn get_string(data string, field_name string) string {
|
||||
for line in data.split_into_lines() {
|
||||
key_val := line.split('=')
|
||||
if key_val[0] == field_name {
|
||||
return key_val[1]
|
||||
}
|
||||
}
|
||||
return ''
|
||||
}
|
||||
|
||||
fn get_int(data string, field string) int {
|
||||
return get_string(data, field).int()
|
||||
}
|
||||
|
||||
// `decode<User>` generates:
|
||||
// fn decode_User(data string) User {
|
||||
// mut result := User{}
|
||||
// result.name = get_string(data, 'name')
|
||||
// result.age = get_int(data, 'age')
|
||||
// return result
|
||||
// }
|
||||
@@ -0,0 +1,18 @@
|
||||
import time
|
||||
|
||||
// Simulate expensive computing using sleep function
|
||||
fn expensive_computing(id int, duration int) {
|
||||
println('Executing expensive computing task (${id})...')
|
||||
time.sleep(duration * time.millisecond)
|
||||
println('Finish task ${id} on ${duration} ms')
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut threads := []thread{}
|
||||
threads << spawn expensive_computing(1, 100)
|
||||
threads << spawn expensive_computing(2, 500)
|
||||
threads << spawn expensive_computing(3, 1000)
|
||||
// Join all tasks
|
||||
threads.wait()
|
||||
println('All jobs finished!')
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
import net.http
|
||||
import sync
|
||||
import time
|
||||
|
||||
fn vlang_time(mut wg sync.WaitGroup) !string {
|
||||
start := time.ticks()
|
||||
data := http.get('https://vlang.io/utc_now')!
|
||||
finish := time.ticks()
|
||||
println('Finish getting time ${finish - start} ms')
|
||||
println(data.body)
|
||||
wg.done()
|
||||
return data.body
|
||||
}
|
||||
|
||||
fn remote_ip(mut wg sync.WaitGroup) !string {
|
||||
start := time.ticks()
|
||||
data := http.get('https://api.ipify.org')!
|
||||
finish := time.ticks()
|
||||
println('Finish getting ip ${finish - start} ms')
|
||||
println(data.body)
|
||||
wg.done()
|
||||
return data.body
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut wg := sync.new_waitgroup()
|
||||
wg.add(2)
|
||||
// Run tasks async
|
||||
spawn vlang_time(mut wg)
|
||||
spawn remote_ip(mut wg)
|
||||
wg.wait()
|
||||
}
|
||||
@@ -0,0 +1,13 @@
|
||||
fn expensive_computing(i int) int {
|
||||
return i * i
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut threads := []thread int{}
|
||||
for i in 1 .. 10 {
|
||||
threads << spawn expensive_computing(i)
|
||||
}
|
||||
// Join all tasks
|
||||
r := threads.wait()
|
||||
println('All jobs finished: ${r}')
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
module main
|
||||
|
||||
fn main() {
|
||||
th := spawn my_print1()
|
||||
th.wait()
|
||||
th2 := spawn my_print2()
|
||||
th2.wait()
|
||||
}
|
||||
|
||||
// default stack size
|
||||
fn my_print1() {
|
||||
println('hello word')
|
||||
}
|
||||
|
||||
// 2MB stack size
|
||||
@[spawn_stack: 2097152]
|
||||
fn my_print2() {
|
||||
println('ahoj svet')
|
||||
}
|
||||
Whitespace-only changes.
@@ -0,0 +1,36 @@
|
||||
// vtest build: false // This should be build with: `v -use-coroutines coroutine_benchs.v`
|
||||
// Note: the Photon wrapper is not yet trivial enough to build/install on the CI.
|
||||
import coroutines
|
||||
import time
|
||||
import net.http
|
||||
import sync
|
||||
|
||||
const run_time = 10 * time.second
|
||||
|
||||
fn request(mut mu sync.Mutex, count &int) {
|
||||
for {
|
||||
http.get('http://vlang.io/utc_now') or { panic(err) }
|
||||
mu.lock()
|
||||
unsafe {
|
||||
(*count)++
|
||||
}
|
||||
mu.unlock()
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut mu := sync.new_mutex()
|
||||
mut count := 0
|
||||
|
||||
for _ in 0 .. 8 {
|
||||
go request(mut mu, &count)
|
||||
}
|
||||
$if is_coroutine ? {
|
||||
println('IS COROUTINE=true')
|
||||
coroutines.sleep(run_time)
|
||||
} $else {
|
||||
println('IS COROUTINE=false')
|
||||
time.sleep(run_time)
|
||||
}
|
||||
println('${count} requests made.')
|
||||
}
|
||||
@@ -0,0 +1,62 @@
|
||||
// vtest build: false // This should be build with: v -use-coroutines simple_coroutines.v
|
||||
// Note: the Photon wrapper is not yet trivial enough to build/install on the CI.
|
||||
import coroutines
|
||||
import time
|
||||
import os
|
||||
import net.http
|
||||
|
||||
fn foo(a int) {
|
||||
for {
|
||||
println('1hello from foo() a=${a}')
|
||||
// C.printf(c'hello from foo() a=%d\n', a)
|
||||
coroutines.sleep(1 * time.second)
|
||||
}
|
||||
}
|
||||
|
||||
fn foo2(a int) {
|
||||
mut i := 0
|
||||
for {
|
||||
println('hello from foo2() a=${a}')
|
||||
// C.printf(c'hello from foo2() a=%d\n', a)
|
||||
coroutines.sleep(2 * time.second)
|
||||
i++
|
||||
resp := http.get('https://vlang.io/utc_now') or { panic(err) }
|
||||
println(resp)
|
||||
mut f := os.create('/tmp/FOO2_a${i}') or { panic(err) }
|
||||
f.write_string(resp.body) or { panic(err) }
|
||||
f.close()
|
||||
}
|
||||
}
|
||||
|
||||
fn foo3(a int) {
|
||||
for {
|
||||
println('hello from foo3() a=${a}')
|
||||
// C.printf(c'hello from foo3() a=%d\n', a)
|
||||
coroutines.sleep(3 * time.second)
|
||||
}
|
||||
}
|
||||
|
||||
fn foo4(a int) {
|
||||
for {
|
||||
println('hello from foo4() a=${a}')
|
||||
// C.printf(c'hello from foo4() a=%d\n', a)
|
||||
coroutines.sleep(3 * time.second)
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
go foo(10)
|
||||
go foo2(20)
|
||||
go foo3(30)
|
||||
go foo3(40)
|
||||
$if is_coroutine ? {
|
||||
println('IS COROUTINE=true')
|
||||
} $else {
|
||||
println('IS COROUTINE=false')
|
||||
}
|
||||
for {
|
||||
println('hello from MAIN')
|
||||
coroutines.sleep(1 * time.second)
|
||||
}
|
||||
println('done')
|
||||
}
|
||||
@@ -0,0 +1,63 @@
|
||||
Note: To more deep study see https://en.wikibooks.org/wiki/X86_Assembly
|
||||
|
||||
# SSE and MMX Extensions
|
||||
|
||||
This document provides an overview of the SSE and MMX extensions used in the project.
|
||||
|
||||
## Table of Contents
|
||||
|
||||
- [Introduction](#introduction)
|
||||
- [SSE Extensions](#sse-extensions)
|
||||
- [MMX Extensions](#mmx-extensions)
|
||||
- [Usage](#usage)
|
||||
|
||||
## Introduction
|
||||
|
||||
SSE (Streaming SIMD Extensions) and MMX (MultiMedia eXtensions) are instruction sets used to
|
||||
enhance the performance of multimedia and signal processing applications.
|
||||
|
||||
## SSE Extensions
|
||||
|
||||
SSE extensions provide a set of instructions that can handle multiple data with a single
|
||||
instruction, improving the performance of applications that require heavy mathematical
|
||||
computations.
|
||||
|
||||
from: [wikibooks](https://en.wikibooks.org/wiki/X86_Assembly/SSE#SSE_Instruction_Set)
|
||||
There are literally hundreds of SSE instructions, some of which are capable of much more than
|
||||
simple SIMD arithmetic. For more in-depth references take a look at the resources chapter of this
|
||||
book.
|
||||
|
||||
You may notice that many floating point SSE instructions end with something like PS or SD. These
|
||||
suffixes differentiate between different versions of the operation. The first letter describes
|
||||
whether the instruction should be Packed or Scalar. Packed operations are applied to every member
|
||||
of the register, while scalar operations are applied to only the first value. For example, in
|
||||
pseudo-code, a packed add would be executed as:
|
||||
|
||||
```
|
||||
v1[0] = v1[0] + v2[0]
|
||||
v1[1] = v1[1] + v2[1]
|
||||
v1[2] = v1[2] + v2[2]
|
||||
v1[3] = v1[3] + v2[3]
|
||||
```
|
||||
|
||||
While a scalar add would only be:
|
||||
|
||||
```
|
||||
v1[0] = v1[0] + v2[0]
|
||||
```
|
||||
|
||||
The second letter refers to the data size: either Single or Double. This simply tells the
|
||||
processor whether to use the register as four 32-bit floats or two 64-bit doubles, respectively.
|
||||
|
||||
## MMX Extensions
|
||||
|
||||
MMX extensions are designed to accelerate multimedia and communication applications by providing
|
||||
instructions that can process multiple data elements in parallel.
|
||||
|
||||
## Usage
|
||||
|
||||
To use these extensions in your project, ensure that your compiler supports them and that you have
|
||||
enabled the appropriate flags.
|
||||
On Linux, you can run the command `lscpu`
|
||||
|
||||
Note: the examples here will compile, but not run on CPU architectures != amd64, like ARM or RISCV .
|
||||
@@ -0,0 +1,39 @@
|
||||
// MMX Instruction Set
|
||||
// Several suffixes are used to indicate what data size the instruction operates on:
|
||||
// Byte (8 bits)
|
||||
// Word (16 bits)
|
||||
// Double word (32 bits)
|
||||
// Quad word (64 bits)
|
||||
// The signedness of the operation is also signified by the suffix: US for unsigned and S for signed.
|
||||
// For example, PSUBUSB subtracts unsigned bytes, while PSUBSD subtracts signed double words.
|
||||
// MMX defined over 40 new instructions, listed below.
|
||||
// EMMS, MOVD, MOVQ, PACKSSDW, PACKSSWB, PACKUSWB, PADDB, PADDD, PADDSB, PADDSW, PADDUSB, PADDUSW,
|
||||
// PADDW, PAND, PANDN, PCMPEQB, PCMPEQD, PCMPEQW, PCMPGTB, PCMPGTD, PCMPGTW, PMADDWD, PMULHW, PMULLW,
|
||||
// POR, PSLLD, PSLLQ, PSLLW, PSRAD, PSRAW, PSRLD, PSRLQ, PSRLW, PSUBB, PSUBD, PSUBSB, PSUBSW, PSUBUSB,
|
||||
// PSUBUSW, PSUBW, PUNPCKHBW, PUNPCKHDQ, PUNPCKHWD, PUNPCKLBW, PUNPCKLDQ, PUNPCKLWD, PXOR
|
||||
|
||||
@[if amd64 && !tinyc && !msvc]
|
||||
fn add_vectors_mmx(a &u8, b &u8, result &u8) {
|
||||
unsafe {
|
||||
asm volatile amd64 {
|
||||
movq mm0, [a] // Load 8 bytes from a into MMX register mm0
|
||||
movq mm1, [b] // Load 8 bytes from b into MMX register mm1
|
||||
paddb mm0, mm1 // Add the two vectors using MMX instruction
|
||||
movq [result], mm0 // Store the result back to memory
|
||||
; ; r (a)
|
||||
r (b)
|
||||
r (result)
|
||||
; mm0
|
||||
mm1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a := [u8(1), 2, 3, 4, 5, 6, 7, 8]
|
||||
b := [u8(8), 7, 6, 5, 4, 3, 2, 1]
|
||||
result := []u8{len: 8}
|
||||
add_vectors_mmx(&a[0], &b[0], &result[0])
|
||||
println(result)
|
||||
assert result == [u8(9), 9, 9, 9, 9, 9, 9, 9]
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
// SSE Instruction Set
|
||||
// SSE: Added with Pentium III
|
||||
// Floating-point Instructions:
|
||||
// ADDPS, ADDSS, CMPPS, CMPSS, COMISS, CVTPI2PS, CVTPS2PI, CVTSI2SS, CVTSS2SI, CVTTPS2PI, CVTTSS2SI,
|
||||
// DIVPS, DIVSS, LDMXCSR, MAXPS, MAXSS, MINPS, MINSS, MOVAPS, MOVHLPS, MOVHPS, MOVLHPS, MOVLPS,
|
||||
// MOVMSKPS, MOVNTPS, MOVSS, MOVUPS, MULPS, MULSS, RCPPS, RCPSS, RSQRTPS, RSQRTSS, SHUFPS, SQRTPS,
|
||||
// SQRTSS, STMXCSR, SUBPS, SUBSS, UCOMISS, UNPCKHPS, UNPCKLPS
|
||||
//
|
||||
// Integer Instructions:
|
||||
// ANDNPS, ANDPS, ORPS, PAVGB, PAVGW, PEXTRW, PINSRW, PMAXSW, PMAXUB, PMINSW, PMINUB, PMOVMSKB, PMULHUW, PSADBW, PSHUFW, XORPS
|
||||
// The ADDPS instruction adds two vectors of floats using SSE instructions.
|
||||
|
||||
@[if amd64 && !tinyc && !msvc]
|
||||
fn add_vectors_sse(a &f32, b &f32, result &f32) {
|
||||
unsafe {
|
||||
asm volatile amd64 {
|
||||
movups xmm0, [a] // Load 4 floats from array a into SSE register xmm0
|
||||
movups xmm1, [b] // Load 4 floats from array b into SSE register xmm1
|
||||
addps xmm0, xmm1 // Add the two vectors using SSE instruction
|
||||
movups [result], xmm0 // Store the result back to memory
|
||||
; ; r (a)
|
||||
r (b)
|
||||
r (result)
|
||||
; xmm0
|
||||
xmm1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a := [f32(1.0), 2.0, 3.0, 4.0]
|
||||
b := [f32(4.0), 3.0, 2.0, 1.0]
|
||||
result := []f32{len: 4}
|
||||
add_vectors_sse(&a[0], &b[0], &result[0])
|
||||
println(result)
|
||||
assert result == [f32(5.0), 5.0, 5.0, 5.0]
|
||||
}
|
||||
@@ -0,0 +1,42 @@
|
||||
// SSE Instruction Set
|
||||
// SSE2: Added with Pentium 4
|
||||
// Floating-point Instructions:
|
||||
// ADDPD, ADDSD, ANDNPD, ANDPD, CMPPD, CMPSD*, COMISD, CVTDQ2PD, CVTDQ2PS, CVTPD2DQ, CVTPD2PI,
|
||||
// CVTPD2PS, CVTPI2PD, CVTPS2DQ, CVTPS2PD, CVTSD2SI, CVTSD2SS, CVTSI2SD, CVTSS2SD, CVTTPD2DQ,
|
||||
// CVTTPD2PI, CVTTPS2DQ, CVTTSD2SI, DIVPD, DIVSD, MAXPD, MAXSD, MINPD, MINSD, MOVAPD, MOVHPD,
|
||||
// MOVLPD, MOVMSKPD, MOVSD*, MOVUPD, MULPD, MULSD, ORPD, SHUFPD, SQRTPD, SQRTSD, SUBPD, SUBSD,
|
||||
// UCOMISD, UNPCKHPD, UNPCKLPD, XORPD
|
||||
// * CMPSD and MOVSD have the same name as the string instruction mnemonics CMPSD (CMPS) and
|
||||
// MOVSD (MOVS); however, the former refer to scalar double-precision floating-points whereas
|
||||
// the latter refer to doubleword strings.
|
||||
// Integer Instructions:
|
||||
// MOVDQ2Q, MOVDQA, MOVDQU, MOVQ2DQ, PADDQ, PSUBQ, PMULUDQ, PSHUFHW, PSHUFLW, PSHUFD, PSLLDQ, PSRLDQ, PUNPCKHQDQ, PUNPCKLQDQ
|
||||
// The MULPD instruction multiplies two vectors of doubles using SSE2 instructions.
|
||||
|
||||
@[if amd64 && !tinyc && !msvc]
|
||||
fn multiply_vectors_sse2(a &f64, b &f64, result &f64) {
|
||||
unsafe {
|
||||
asm volatile amd64 {
|
||||
movupd xmm0, [a] // Load 2 doubles from array a into SSE2 register xmm0
|
||||
movupd xmm1, [b] // Load 2 doubles from array b into SSE2 register xmm1
|
||||
mulpd xmm0, xmm1 // Multiply the two vectors using SSE2 instruction
|
||||
movupd [result], xmm0 // Store the result back to memory
|
||||
; ; r (a)
|
||||
r (b)
|
||||
r (result)
|
||||
; xmm0
|
||||
xmm1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a := [f64(1.5), 2.5]
|
||||
b := [f64(3.5), 4.5]
|
||||
result := []f64{len: 2}
|
||||
multiply_vectors_sse2(&a[0], &b[0], &result[0])
|
||||
println(result)
|
||||
// 5.25 = 1.5 * 3.5
|
||||
// 11.25 = 2.5 * 4.5
|
||||
assert result == [f64(5.25), 11.25]
|
||||
}
|
||||
@@ -0,0 +1,36 @@
|
||||
// SSE Instruction Set
|
||||
// SSE3: Added with later Pentium 4
|
||||
// ADDSUBPD, ADDSUBPS, HADDPD, HADDPS, HSUBPD, HSUBPS, MOVDDUP, MOVSHDUP, MOVSLDUP
|
||||
// The HADDPS instruction performs horizontal addition of two vectors of floats using SSE3
|
||||
// instructions.
|
||||
|
||||
@[if amd64 && !tinyc && !msvc]
|
||||
fn horizontal_add_sse3(a &f32, b &f32, result &f32) {
|
||||
unsafe {
|
||||
asm volatile amd64 {
|
||||
movaps xmm0, [a] // Load 4 floats from array a into SSE3 register xmm0
|
||||
movaps xmm1, [b] // Load 4 floats from array b into SSE3 register xmm1
|
||||
haddps xmm0, xmm1 // Perform horizontal add of xmm0 and xmm1
|
||||
movaps [result], xmm0 // Store the result back to memory
|
||||
; ; r (a)
|
||||
r (b)
|
||||
r (result)
|
||||
; xmm0
|
||||
xmm1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a := [f32(1.0), 2.0, 3.0, 4.0]
|
||||
b := [f32(5.0), 6.0, 7.0, 8.0]
|
||||
result := []f32{len: 4}
|
||||
horizontal_add_sse3(&a[0], &b[0], &result[0])
|
||||
println(result)
|
||||
// The result should be [3.0, 7.0, 11.0, 15.0] due to horizontal addition.
|
||||
// 1.0 + 2.0 = 3.0
|
||||
// 3.0 + 4.0 = 7.0
|
||||
// 5.0 + 6.0 = 11.0
|
||||
// 7.0 + 8.0 = 15.0
|
||||
assert result == [f32(3.0), 7.0, 11.0, 15.0]
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// SSE Instruction Set
|
||||
// SSE4.1: Added with later Core 2
|
||||
// MPSADBW, PHMINPOSUW, PMULLD, PMULDQ, DPPS, DPPD, BLENDPS, BLENDPD, BLENDVPS, BLENDVPD,
|
||||
// PBLENDVB, PBLENDW, PMINSB, PMAXSB, PMINUW, PMAXUW, PMINUD, PMAXUD, PMINSD, PMAXSD, ROUNDPS,
|
||||
// ROUNDSS, ROUNDPD, ROUNDSD, INSERTPS, PINSRB, PINSRD, PINSRQ, EXTRACTPS, PEXTRB, PEXTRW,
|
||||
// PEXTRD, PEXTRQ, PMOVSXBW, PMOVZXBW, PMOVSXBD, PMOVZXBD, PMOVSXBQ, PMOVZXBQ, PMOVSXWD,
|
||||
// PMOVZXWD, PMOVSXWQ, PMOVZXWQ, PMOVSXDQ, PMOVZXDQ, PTEST, PCMPEQQ, PACKUSDW, MOVNTDQA
|
||||
|
||||
@[if amd64 && !tinyc && !msvc]
|
||||
fn round_floats_sse4_1(a &f32, result &f32) {
|
||||
unsafe {
|
||||
asm volatile amd64 {
|
||||
movups xmm0, [a] // Load 4 floats from array a into xmm0
|
||||
roundps xmm0, xmm0, 0 // Round to nearest integer
|
||||
movups [result], xmm0 // Store the result in result array
|
||||
; ; r (a)
|
||||
r (result)
|
||||
; xmm0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a := [f32(1.2), 2.5, 3.8, 4.4]
|
||||
result := []f32{len: 4}
|
||||
// Rounding mode 0 corresponds to rounding to the nearest integer
|
||||
round_floats_sse4_1(&a[0], &result[0])
|
||||
println(result)
|
||||
// The expected rounded result should be [1.0, 2.0, 4.0, 4.0]
|
||||
assert result == [f32(1.0), 2.0, 4.0, 4.0]
|
||||
}
|
||||
@@ -0,0 +1,31 @@
|
||||
// SSE Instruction Set
|
||||
// SSSE3: Added with Xeon 5100 and early Core 2
|
||||
// PSIGNW, PSIGND, PSIGNB, PSHUFB, PMULHRSW, PMADDUBSW, PHSUBW, PHSUBSW, PHSUBD, PHADDW, PHADDSW,
|
||||
// PHADDD, PALIGNR, PABSW, PABSD, PABSB
|
||||
// The PSIGNW instruction negates or leaves elements unchanged based on another vector's signs.
|
||||
|
||||
@[if amd64 && !tinyc && !msvc]
|
||||
fn psignw_example(a &i16, b &i16, result &i16) {
|
||||
unsafe {
|
||||
asm volatile amd64 {
|
||||
movdqa xmm0, [a] // Load 8 signed 16-bit integers from array a into xmm0
|
||||
movdqa xmm1, [b] // Load 8 signed 16-bit integers from array b into xmm1
|
||||
psignw xmm0, xmm1 // Adjust the sign of elements in xmm0 based on xmm1
|
||||
movdqa [result], xmm0 // Store the result back to memory
|
||||
; ; r (a)
|
||||
r (b)
|
||||
r (result)
|
||||
; xmm0
|
||||
xmm1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
a0 := [i16(1), -2, 3, -4, 5, -6, 7, -8]
|
||||
b0 := [i16(1), -1, 1, -1, 1, -1, 1, -1]
|
||||
result0 := []i16{len: 8}
|
||||
psignw_example(&a0[0], &b0[0], &result0[0])
|
||||
dump(result0)
|
||||
assert result0 == [i16(1), 2, 3, 4, 5, 6, 7, 8]
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import semver
|
||||
|
||||
fn main() {
|
||||
semver.from('asd') or { check_error(err) }
|
||||
semver.from('') or { check_error(err) }
|
||||
}
|
||||
|
||||
fn check_error(err IError) {
|
||||
match err {
|
||||
semver.InvalidVersionFormatError {
|
||||
println('wrong format')
|
||||
}
|
||||
semver.EmptyInputError {
|
||||
println('empty input')
|
||||
}
|
||||
else {
|
||||
println('unknown error')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
// vtest build: !(macos || windows)
|
||||
import db.mysql
|
||||
|
||||
fn main() {
|
||||
mut conn := mysql.connect(
|
||||
host: 'localhost'
|
||||
port: 3306
|
||||
username: 'root'
|
||||
password: ''
|
||||
dbname: 'mysql'
|
||||
)!
|
||||
res := conn.query('show tables')!
|
||||
for row in res.rows() {
|
||||
println(row.vals.join(', '))
|
||||
}
|
||||
conn.close()!
|
||||
}
|
||||
@@ -0,0 +1,161 @@
|
||||
// vtest build: !(macos || windows)
|
||||
import db.mysql
|
||||
import pool
|
||||
import time
|
||||
|
||||
// Define your connection factory function
|
||||
fn create_conn() !&pool.ConnectionPoolable {
|
||||
config := mysql.Config{
|
||||
host: '127.0.0.1'
|
||||
port: 3306
|
||||
username: 'root'
|
||||
password: '12345678'
|
||||
dbname: 'mysql'
|
||||
}
|
||||
db := mysql.connect(config)!
|
||||
return &db
|
||||
}
|
||||
|
||||
fn main() {
|
||||
// Configure pool parameters
|
||||
config := pool.ConnectionPoolConfig{
|
||||
max_conns: 50
|
||||
min_idle_conns: 5
|
||||
max_lifetime: 2 * time.hour
|
||||
idle_timeout: 30 * time.minute
|
||||
get_timeout: 5 * time.second
|
||||
}
|
||||
|
||||
// Create connection pool
|
||||
mut my_pool := pool.new_connection_pool(create_conn, config)!
|
||||
defer {
|
||||
// When application exits
|
||||
my_pool.close()
|
||||
}
|
||||
|
||||
// Acquire connection
|
||||
mut conn := my_pool.get()!
|
||||
defer {
|
||||
// Return connection to pool
|
||||
my_pool.put(conn) or { println(err) }
|
||||
}
|
||||
|
||||
// Convert `conn` to a `mysql.DB` object
|
||||
mut db := conn as mysql.DB
|
||||
|
||||
assert db.validate()!
|
||||
|
||||
mut response := db.exec('drop table if exists users')!
|
||||
assert response == []mysql.Row{}
|
||||
|
||||
response = db.exec('create table if not exists users (
|
||||
id INT PRIMARY KEY AUTO_INCREMENT,
|
||||
username TEXT,
|
||||
last_name TEXT NULL DEFAULT NULL
|
||||
)')!
|
||||
assert response == []mysql.Row{}
|
||||
|
||||
mut result_code := db.exec_none('insert into users (username) values ("jackson")')
|
||||
assert result_code == 0
|
||||
result_code = db.exec_none('insert into users (username) values ("shannon")')
|
||||
assert result_code == 0
|
||||
result_code = db.exec_none('insert into users (username) values ("bailey")')
|
||||
assert result_code == 0
|
||||
result_code = db.exec_none('insert into users (username) values ("blaze")')
|
||||
assert result_code == 0
|
||||
rows := db.exec_param('insert into users (username) values (?)', 'Hi')!
|
||||
assert rows == []mysql.Row{}
|
||||
|
||||
// Regression testing to ensure the query and exec return the same values
|
||||
res := db.query('select * from users')!
|
||||
response = res.rows()
|
||||
assert response[0].vals[1] == 'jackson'
|
||||
response = db.exec('select * from users')!
|
||||
assert response[0].vals[1] == 'jackson'
|
||||
|
||||
response = db.exec('select * from users where id = 400')!
|
||||
assert response.len == 0
|
||||
|
||||
single_row := db.exec_one('select * from users')!
|
||||
assert single_row.vals[1] == 'jackson'
|
||||
|
||||
response = db.exec_param_many('select * from users where username = ?', [
|
||||
'jackson',
|
||||
])!
|
||||
assert response[0] == mysql.Row{
|
||||
vals: ['1', 'jackson', '']
|
||||
}
|
||||
|
||||
response = db.exec_param_many('select * from users where username = ? and id = ?', [
|
||||
'bailey',
|
||||
'3',
|
||||
])!
|
||||
assert response[0] == mysql.Row{
|
||||
vals: ['3', 'bailey', '']
|
||||
}
|
||||
|
||||
response = db.exec_param_many('select * from users', [''])!
|
||||
assert response == [
|
||||
mysql.Row{
|
||||
vals: ['1', 'jackson', '']
|
||||
},
|
||||
mysql.Row{
|
||||
vals: ['2', 'shannon', '']
|
||||
},
|
||||
mysql.Row{
|
||||
vals: ['3', 'bailey', '']
|
||||
},
|
||||
mysql.Row{
|
||||
vals: ['4', 'blaze', '']
|
||||
},
|
||||
mysql.Row{
|
||||
vals: ['5', 'Hi', '']
|
||||
},
|
||||
]
|
||||
|
||||
response = db.exec_param('select * from users where username = ?', 'blaze')!
|
||||
assert response[0] == mysql.Row{
|
||||
vals: ['4', 'blaze', '']
|
||||
}
|
||||
|
||||
// transaction test
|
||||
// turn off `autocommit` first
|
||||
db.autocommit(false)!
|
||||
// begin a new transaction
|
||||
db.begin()!
|
||||
result_code = db.exec_none('insert into users (username) values ("tom")')
|
||||
assert result_code == 0
|
||||
// make a savepoint
|
||||
db.savepoint('savepoint1')!
|
||||
result_code = db.exec_none('insert into users (username) values ("kitty")')
|
||||
assert result_code == 0
|
||||
// rollback to `savepoint1`
|
||||
db.rollback_to('savepoint1')!
|
||||
result_code = db.exec_none('insert into users (username) values ("mars")')
|
||||
assert result_code == 0
|
||||
db.commit()!
|
||||
response = db.exec_param_many('select * from users', [''])!
|
||||
assert response == [
|
||||
mysql.Row{
|
||||
vals: ['1', 'jackson', '']
|
||||
},
|
||||
mysql.Row{
|
||||
vals: ['2', 'shannon', '']
|
||||
},
|
||||
mysql.Row{
|
||||
vals: ['3', 'bailey', '']
|
||||
},
|
||||
mysql.Row{
|
||||
vals: ['4', 'blaze', '']
|
||||
},
|
||||
mysql.Row{
|
||||
vals: ['5', 'Hi', '']
|
||||
},
|
||||
mysql.Row{
|
||||
vals: ['6', 'tom', '']
|
||||
},
|
||||
mysql.Row{
|
||||
vals: ['8', 'mars', '']
|
||||
},
|
||||
]
|
||||
}
|
||||
@@ -0,0 +1,293 @@
|
||||
// vtest build: !(macos || windows)
|
||||
import os
|
||||
import db.sqlite
|
||||
import db.mysql
|
||||
import db.pg
|
||||
|
||||
// The goal of this example, is to show how you can connect to
|
||||
// several different databases in the same program, and use both
|
||||
// the ORM and the native connection wrapper, that each DB driver
|
||||
// provides, if you need to execute more complex SQL queries.
|
||||
//
|
||||
// You can use environment variables to pass your local DB connection
|
||||
// settings, without editing the code, like this:
|
||||
//
|
||||
// MUSER='myuser' MPASS='abc' MDATABASE='vtestdb' PGUSER='postgres' PGPASS='password' PGDATABASE='postgres' ./v -g run examples/database/orm.v
|
||||
//
|
||||
// WARNING: this example will drop and re-create any tables named:
|
||||
// * modules
|
||||
// * User
|
||||
// * Parent
|
||||
// * Child
|
||||
// in the passed databases, so it is better to use empty DBs for it.
|
||||
|
||||
const mysql_host = os.getenv_opt('MHOST') or { 'localhost' }
|
||||
const mysql_port = os.getenv_opt('MPORT') or { '3306' }.u32()
|
||||
const mysql_user = os.getenv_opt('MUSER') or { 'myuser' }
|
||||
const mysql_pass = os.getenv_opt('MPASS') or { 'abc' }
|
||||
const mysql_db = os.getenv_opt('MDATABASE') or { 'test' }
|
||||
|
||||
const pg_host = os.getenv_opt('PGHOST') or { 'localhost' }
|
||||
const pg_user = os.getenv_opt('PGUSER') or { 'test' }
|
||||
const pg_pass = os.getenv_opt('PGPASS') or { 'abc' }
|
||||
const pg_db = os.getenv_opt('PGDATABASE') or { 'test' }
|
||||
|
||||
@[table: 'modules']
|
||||
struct Module {
|
||||
id int @[primary; sql: serial]
|
||||
name string
|
||||
nr_downloads int @[sql: u64]
|
||||
creator User
|
||||
}
|
||||
|
||||
struct User {
|
||||
id int @[primary; sql: serial]
|
||||
age u32 @[unique: 'user']
|
||||
name string @[sql: 'username'; sql_type: 'VARCHAR(200)'; unique]
|
||||
is_customer bool @[sql: 'abc'; unique: 'user']
|
||||
skipped_string string @[skip]
|
||||
}
|
||||
|
||||
struct Parent {
|
||||
id int @[primary; sql: serial]
|
||||
name string
|
||||
children []Child @[fkey: 'parent_id']
|
||||
}
|
||||
|
||||
struct Child {
|
||||
id int @[primary; sql: serial]
|
||||
parent_id int
|
||||
name string
|
||||
}
|
||||
|
||||
fn sqlite3_array() ! {
|
||||
eprintln('------------ ${@METHOD} -----------------')
|
||||
mut db := sqlite.connect(':memory:')!
|
||||
defer {
|
||||
sql db {
|
||||
drop table Parent
|
||||
drop table Child
|
||||
} or {}
|
||||
db.close() or {}
|
||||
}
|
||||
|
||||
sql db {
|
||||
create table Parent
|
||||
}!
|
||||
sql db {
|
||||
create table Child
|
||||
}!
|
||||
par := Parent{
|
||||
name: 'test'
|
||||
children: [
|
||||
Child{
|
||||
name: 'abc'
|
||||
},
|
||||
Child{
|
||||
name: 'def'
|
||||
},
|
||||
]
|
||||
}
|
||||
sql db {
|
||||
insert par into Parent
|
||||
}!
|
||||
parent := sql db {
|
||||
select from Parent where id == 1
|
||||
}!
|
||||
eprintln(parent)
|
||||
}
|
||||
|
||||
fn msql_array() ! {
|
||||
eprintln('------------ ${@METHOD} -----------------')
|
||||
mut db := mysql.connect(
|
||||
host: mysql_host
|
||||
port: mysql_port
|
||||
username: mysql_user
|
||||
password: mysql_pass
|
||||
dbname: mysql_db
|
||||
)!
|
||||
defer {
|
||||
sql db {
|
||||
drop table Parent
|
||||
} or {}
|
||||
db.close() or {}
|
||||
}
|
||||
|
||||
db.query('drop table if exists Parent')!
|
||||
db.query('drop table if exists Child')!
|
||||
sql db {
|
||||
create table Parent
|
||||
create table Child
|
||||
}!
|
||||
par := Parent{
|
||||
name: 'test'
|
||||
children: [
|
||||
Child{
|
||||
name: 'abc'
|
||||
},
|
||||
Child{
|
||||
name: 'def'
|
||||
},
|
||||
]
|
||||
}
|
||||
sql db {
|
||||
insert par into Parent
|
||||
}!
|
||||
parent := sql db {
|
||||
select from Parent where id == 1
|
||||
}!
|
||||
eprintln(parent)
|
||||
}
|
||||
|
||||
fn psql_array() ! {
|
||||
eprintln('------------ ${@METHOD} -----------------')
|
||||
mut db := pg.connect(host: pg_host, user: pg_user, password: pg_pass, dbname: pg_db)!
|
||||
defer {
|
||||
db.exec_one('drop table if exists "Parent", "Child"') or { eprintln(err) }
|
||||
db.close() or {}
|
||||
}
|
||||
db.exec_one('drop table if exists "Parent", "Child"') or { eprintln(err) }
|
||||
|
||||
sql db {
|
||||
create table Parent
|
||||
create table Child
|
||||
}!
|
||||
par := Parent{
|
||||
name: 'test'
|
||||
children: [
|
||||
Child{
|
||||
name: 'abc'
|
||||
},
|
||||
Child{
|
||||
name: 'def'
|
||||
},
|
||||
]
|
||||
}
|
||||
sql db {
|
||||
insert par into Parent
|
||||
}!
|
||||
parent := sql db {
|
||||
select from Parent where id == 1
|
||||
}!
|
||||
eprintln(parent)
|
||||
}
|
||||
|
||||
fn sqlite3() ! {
|
||||
eprintln('------------ ${@METHOD} -----------------')
|
||||
mut db := sqlite.connect(':memory:')!
|
||||
defer {
|
||||
sql db {
|
||||
drop table Module
|
||||
drop table User
|
||||
} or {}
|
||||
db.close() or {}
|
||||
}
|
||||
|
||||
sql db {
|
||||
create table Module
|
||||
}!
|
||||
sql db {
|
||||
create table User
|
||||
}!
|
||||
mod := Module{
|
||||
name: 'test'
|
||||
nr_downloads: 10
|
||||
creator: User{
|
||||
age: 21
|
||||
name: 'VUser'
|
||||
is_customer: true
|
||||
}
|
||||
}
|
||||
sql db {
|
||||
insert mod into Module
|
||||
}!
|
||||
modul := sql db {
|
||||
select from Module where id == 1
|
||||
}!
|
||||
eprintln(modul)
|
||||
}
|
||||
|
||||
fn msql() ! {
|
||||
eprintln('------------ ${@METHOD} -----------------')
|
||||
mut conn := mysql.connect(
|
||||
host: mysql_host
|
||||
port: mysql_port
|
||||
username: mysql_user
|
||||
password: mysql_pass
|
||||
dbname: mysql_db
|
||||
)!
|
||||
defer {
|
||||
conn.query('DROP TABLE IF EXISTS Module') or { eprintln(err) }
|
||||
conn.query('DROP TABLE IF EXISTS User') or { eprintln(err) }
|
||||
conn.close() or {}
|
||||
}
|
||||
conn.query('DROP TABLE IF EXISTS Module') or { eprintln(err) }
|
||||
conn.query('DROP TABLE IF EXISTS User') or { eprintln(err) }
|
||||
|
||||
sql conn {
|
||||
create table Module
|
||||
}!
|
||||
sql conn {
|
||||
create table User
|
||||
}!
|
||||
mod := Module{
|
||||
name: 'test'
|
||||
nr_downloads: 10
|
||||
creator: User{
|
||||
age: 21
|
||||
name: 'VUser'
|
||||
is_customer: true
|
||||
}
|
||||
}
|
||||
sql conn {
|
||||
insert mod into Module
|
||||
}!
|
||||
m := sql conn {
|
||||
select from Module where id == 1
|
||||
}!
|
||||
eprintln(m)
|
||||
}
|
||||
|
||||
fn psql() ! {
|
||||
eprintln('------------ ${@METHOD} -----------------')
|
||||
mut db := pg.connect(host: pg_host, user: pg_user, password: pg_pass, dbname: pg_db)!
|
||||
defer {
|
||||
db.exec_one('drop table if exists "modules", "User"') or { eprintln(err) }
|
||||
db.close() or {}
|
||||
}
|
||||
db.exec_one('drop table if exists "modules", "User"') or { eprintln(err) }
|
||||
sql db {
|
||||
create table Module
|
||||
create table User
|
||||
}!
|
||||
mod := Module{
|
||||
name: 'test'
|
||||
nr_downloads: 10
|
||||
creator: User{
|
||||
age: 21
|
||||
name: 'VUser'
|
||||
is_customer: true
|
||||
}
|
||||
}
|
||||
sql db {
|
||||
insert mod into Module
|
||||
}!
|
||||
modul := sql db {
|
||||
select from Module where id == 1
|
||||
}!
|
||||
sql db {
|
||||
drop table Module
|
||||
}!
|
||||
eprintln(modul)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
eprintln('------------ ${@METHOD} -----------------')
|
||||
sqlite3_array()!
|
||||
msql_array()!
|
||||
psql_array()!
|
||||
|
||||
sqlite3()!
|
||||
msql()!
|
||||
psql()!
|
||||
}
|
||||
@@ -0,0 +1 @@
|
||||
customer
|
||||
@@ -0,0 +1,71 @@
|
||||
// vtest build: linux
|
||||
module main
|
||||
|
||||
import db.pg
|
||||
|
||||
struct Customer {
|
||||
id int
|
||||
name string
|
||||
nr_orders int
|
||||
country string
|
||||
}
|
||||
|
||||
const dash = '----------------------------------------------------------------'
|
||||
|
||||
fn main() {
|
||||
db := pg.connect(
|
||||
host: 'localhost' // or '127.0.0.1'
|
||||
user: 'postgres'
|
||||
dbname: 'customerdb'
|
||||
) or {
|
||||
eprintln('failed to connect, error: ${err}')
|
||||
return
|
||||
}
|
||||
|
||||
nr_customers := sql db {
|
||||
select count from Customer
|
||||
}!
|
||||
println('Total customers: ${nr_customers}')
|
||||
|
||||
println(dash)
|
||||
bg_country := 'Bulgaria'
|
||||
// V syntax can be used to build queries
|
||||
bg_customers := sql db {
|
||||
select from Customer where country == bg_country && id != 2
|
||||
}!
|
||||
for customer in bg_customers {
|
||||
println('${customer.country} | ${customer.id} - ${customer.name}')
|
||||
}
|
||||
|
||||
println(dash)
|
||||
ru_customers := sql db {
|
||||
select from Customer where country == 'Russia'
|
||||
}!
|
||||
for customer in ru_customers {
|
||||
println('${customer.country} | ${customer.id} - ${customer.name}')
|
||||
}
|
||||
|
||||
// by adding `limit 1` we tell V that there will be only one object
|
||||
println(dash)
|
||||
existing := sql db {
|
||||
select from Customer where id == 1 limit 1
|
||||
}!
|
||||
println('Existing customer name: ${existing}[0].name')
|
||||
println('Existing customer full information:')
|
||||
println(existing)
|
||||
|
||||
println(dash)
|
||||
q := Customer{}
|
||||
// It's easy to handle queries that don't return any data
|
||||
_ := sql db {
|
||||
select from Customer where id == 12345 && name == q.name && nr_orders > q.nr_orders limit 1
|
||||
}!
|
||||
// Insert a new customer
|
||||
nc := Customer{
|
||||
name: 'John Doe'
|
||||
nr_orders: 10
|
||||
}
|
||||
sql db {
|
||||
insert nc into Customer
|
||||
}!
|
||||
}
|
||||
@@ -0,0 +1,122 @@
|
||||
--
|
||||
-- PostgreSQL database dump
|
||||
--
|
||||
|
||||
-- Dumped from database version 9.5.19
|
||||
-- Dumped by pg_dump version 9.5.19
|
||||
|
||||
SET statement_timeout = 0;
|
||||
SET lock_timeout = 0;
|
||||
SET client_encoding = 'UTF8';
|
||||
SET standard_conforming_strings = on;
|
||||
SELECT pg_catalog.set_config('search_path', '', false);
|
||||
SET check_function_bodies = false;
|
||||
SET xmloption = content;
|
||||
SET client_min_messages = warning;
|
||||
SET row_security = off;
|
||||
|
||||
--
|
||||
-- Name: plpgsql; Type: EXTENSION; Schema: -; Owner:
|
||||
--
|
||||
|
||||
CREATE EXTENSION IF NOT EXISTS plpgsql WITH SCHEMA pg_catalog;
|
||||
|
||||
|
||||
--
|
||||
-- Name: EXTENSION plpgsql; Type: COMMENT; Schema: -; Owner:
|
||||
--
|
||||
|
||||
COMMENT ON EXTENSION plpgsql IS 'PL/pgSQL procedural language';
|
||||
|
||||
|
||||
SET default_tablespace = '';
|
||||
|
||||
SET default_with_oids = false;
|
||||
|
||||
--
|
||||
-- Name: customers; Type: TABLE; Schema: public; Owner: myuser
|
||||
--
|
||||
|
||||
CREATE TABLE public.customers (
|
||||
id integer NOT NULL,
|
||||
name text DEFAULT ''::text,
|
||||
nr_orders integer DEFAULT 0,
|
||||
country text DEFAULT 'England'::text,
|
||||
created_at timestamp without time zone DEFAULT now()
|
||||
);
|
||||
|
||||
|
||||
ALTER TABLE public.customers OWNER TO myuser;
|
||||
|
||||
--
|
||||
-- Name: customers_id_seq; Type: SEQUENCE; Schema: public; Owner: myuser
|
||||
--
|
||||
|
||||
CREATE SEQUENCE public.customers_id_seq
|
||||
START WITH 1
|
||||
INCREMENT BY 1
|
||||
NO MINVALUE
|
||||
NO MAXVALUE
|
||||
CACHE 1;
|
||||
|
||||
|
||||
ALTER TABLE public.customers_id_seq OWNER TO myuser;
|
||||
|
||||
--
|
||||
-- Name: customers_id_seq; Type: SEQUENCE OWNED BY; Schema: public; Owner: myuser
|
||||
--
|
||||
|
||||
ALTER SEQUENCE public.customers_id_seq OWNED BY public.customers.id;
|
||||
|
||||
|
||||
--
|
||||
-- Name: id; Type: DEFAULT; Schema: public; Owner: myuser
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.customers ALTER COLUMN id SET DEFAULT nextval('public.customers_id_seq'::regclass);
|
||||
|
||||
|
||||
--
|
||||
-- Data for Name: customers; Type: TABLE DATA; Schema: public; Owner: myuser
|
||||
--
|
||||
|
||||
COPY public.customers (id, name, nr_orders, country, created_at) FROM stdin;
|
||||
2 Pippi Långstrump 3 Bulgaria 2019-08-19 09:41:30.78888
|
||||
1 Bilbo Begins 11 Bulgaria 2019-08-19 09:40:31.396807
|
||||
3 Viktualia Rullgardina 0 Bulgaria 2019-08-19 09:42:52.723223
|
||||
4 Krusmynta Efraimsdotter 5 Bulgaria 2019-08-19 09:43:04.083209
|
||||
5 Ana Karenina 0 Russia 2019-08-20 15:41:50.244971
|
||||
7 Jiji Lolobridgida 0 Italy 2019-08-20 15:42:26.020113
|
||||
6 Viktor Savashkin 8 Russia 2019-08-20 15:42:07.213557
|
||||
\.
|
||||
|
||||
|
||||
--
|
||||
-- Name: customers_id_seq; Type: SEQUENCE SET; Schema: public; Owner: myuser
|
||||
--
|
||||
|
||||
SELECT pg_catalog.setval('public.customers_id_seq', 1, true);
|
||||
|
||||
|
||||
--
|
||||
-- Name: customers_pkey; Type: CONSTRAINT; Schema: public; Owner: myuser
|
||||
--
|
||||
|
||||
ALTER TABLE ONLY public.customers
|
||||
ADD CONSTRAINT customers_pkey PRIMARY KEY (id);
|
||||
|
||||
|
||||
--
|
||||
-- Name: SCHEMA public; Type: ACL; Schema: -; Owner: postgres
|
||||
--
|
||||
|
||||
REVOKE ALL ON SCHEMA public FROM PUBLIC;
|
||||
REVOKE ALL ON SCHEMA public FROM postgres;
|
||||
GRANT ALL ON SCHEMA public TO postgres;
|
||||
GRANT ALL ON SCHEMA public TO PUBLIC;
|
||||
|
||||
|
||||
--
|
||||
-- PostgreSQL database dump complete
|
||||
--
|
||||
|
||||
@@ -0,0 +1,21 @@
|
||||
import db.sqlite
|
||||
|
||||
fn main() {
|
||||
db := sqlite.connect(':memory:')!
|
||||
db.exec("create table users (id integer primary key, name text default '');") or { panic(err) }
|
||||
|
||||
db.exec("insert into users (name) values ('Sam')")!
|
||||
db.exec("insert into users (name) values ('Peter')")!
|
||||
db.exec("insert into users (name) values ('Kate')")!
|
||||
|
||||
nr_users := db.q_int('select count(*) from users')!
|
||||
println('nr users = ${nr_users}')
|
||||
|
||||
name := db.q_string('select name from users where id = 1')!
|
||||
assert name == 'Sam'
|
||||
|
||||
users := db.exec('select * from users')!
|
||||
for row in users {
|
||||
println(row.vals)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
module main
|
||||
|
||||
import arrays.diff
|
||||
import os
|
||||
|
||||
// diff_files generate diff for two files.
|
||||
fn diff_files(src_file string, dst_file string) !string {
|
||||
src := os.read_lines(src_file)!
|
||||
dst := os.read_lines(dst_file)!
|
||||
|
||||
mut ctx := diff.diff(src, dst)
|
||||
return ctx.generate_patch(colorful: true, block_header: true)
|
||||
}
|
||||
|
||||
fn main() {
|
||||
f1 := "Module{
|
||||
name: 'Foo'
|
||||
description: 'Awesome V module.'
|
||||
version: '0.0.0'
|
||||
dependencies: []
|
||||
}
|
||||
"
|
||||
f2 := "Module{
|
||||
name: 'foo'
|
||||
description: 'Awesome V module.'
|
||||
version: '0.1.0'
|
||||
license: 'MIT'
|
||||
dependencies: []
|
||||
}
|
||||
"
|
||||
p1 := 'diff_f1.txt'
|
||||
p2 := 'diff_f2.txt'
|
||||
os.write_file(p1, f1)!
|
||||
os.write_file(p2, f2)!
|
||||
|
||||
str := diff_files(p1, p2)!
|
||||
println(str)
|
||||
|
||||
os.rm(p1)!
|
||||
os.rm(p2)!
|
||||
}
|
||||
@@ -0,0 +1,10 @@
|
||||
fn factorial(n u32) u32 {
|
||||
if dump(n <= 1) {
|
||||
return dump(1)
|
||||
}
|
||||
return dump(n * factorial(n - 1))
|
||||
}
|
||||
|
||||
fn main() {
|
||||
println(factorial(5))
|
||||
}
|
||||
@@ -0,0 +1,21 @@
|
||||
module library
|
||||
|
||||
// add_1 is exported with the C name `add_1`.
|
||||
// It can be called by external programs, when the module is compiled
|
||||
// as a shared library.
|
||||
// It is exported, because the function is declared as public with `pub`.
|
||||
// The exported C name is `add_1`, because of the export: tag.
|
||||
// (Normally, the exported name is a V mangled version based on the module
|
||||
// name followed by __, followed by the fn name, i.e. it would have been
|
||||
// `library__add_1`, if not for the export: tag).
|
||||
@[export: 'add_1']
|
||||
pub fn add_1(x int, y int) int {
|
||||
num := my_private_function(x + y)
|
||||
println('hello from ${@FN} , num = ${num}')
|
||||
return num
|
||||
}
|
||||
|
||||
// this function is not exported and will not be visible to external programs.
|
||||
fn my_private_function(x int) int {
|
||||
return 1 + x
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
module main
|
||||
|
||||
// Note: This program, requires that the shared library was already compiled.
|
||||
// To do so, run `v -d no_backtrace -o library -shared modules/library/library.v`
|
||||
// before running this program.
|
||||
import os
|
||||
import dl
|
||||
import dl.loader
|
||||
|
||||
type FNAdder = fn (int, int) int
|
||||
|
||||
const cfolder = os.dir(@FILE)
|
||||
const default_paths = [
|
||||
os.join_path(cfolder, 'library${dl.dl_ext}'),
|
||||
os.join_path(cfolder, 'location1/library${dl.dl_ext}'),
|
||||
os.join_path(cfolder, 'location2/library${dl.dl_ext}'),
|
||||
os.join_path(cfolder, 'modules/library/library${dl.dl_ext}'),
|
||||
]
|
||||
|
||||
fn main() {
|
||||
mut dl_loader := loader.get_or_create_dynamic_lib_loader(
|
||||
key: cfolder + '/library'
|
||||
paths: default_paths
|
||||
)!
|
||||
defer {
|
||||
dl_loader.unregister()
|
||||
}
|
||||
sym := dl_loader.get_sym('add_1')!
|
||||
f := FNAdder(sym)
|
||||
eprintln('f: ${ptr_str(f)}')
|
||||
res := f(1, 2)
|
||||
eprintln('res: ${res}')
|
||||
}
|
||||
@@ -0,0 +1,67 @@
|
||||
module main
|
||||
|
||||
import os
|
||||
import dl
|
||||
|
||||
const vexe = os.real_path(os.getenv('VEXE'))
|
||||
const so_ext = dl.dl_ext
|
||||
|
||||
fn test_vexe() {
|
||||
// dump(vexe)
|
||||
assert vexe != ''
|
||||
// dump(os.executable())
|
||||
// dump(@FILE)
|
||||
// dump(cfolder)
|
||||
// dump(so_ext)
|
||||
}
|
||||
|
||||
fn test_can_compile_library() {
|
||||
os.chdir(cfolder) or {}
|
||||
library_file_path := os.join_path(cfolder, dl.get_libname('library'))
|
||||
os.rm(library_file_path) or {}
|
||||
v_compile('-d no_backtrace -o library -shared modules/library/library.v')
|
||||
assert os.is_file(library_file_path)
|
||||
}
|
||||
|
||||
fn test_can_compile_main_program() {
|
||||
os.chdir(cfolder) or {}
|
||||
library_file_path := os.join_path(cfolder, dl.get_libname('library'))
|
||||
assert os.is_file(library_file_path)
|
||||
result := v_compile('run use_shared_library.v')
|
||||
// dump(result)
|
||||
assert result.output.contains('hello from add_1 , num = 4')
|
||||
assert result.output.contains('res: 4')
|
||||
os.rm(library_file_path) or {}
|
||||
}
|
||||
|
||||
fn test_can_compile_and_use_library_with_skip_unused_home_dir() {
|
||||
os.chdir(cfolder) or {}
|
||||
library_file_path := os.join_path(cfolder, dl.get_libname('library'))
|
||||
os.rm(library_file_path) or {}
|
||||
v_compile('-skip-unused -d no_backtrace -o library -shared modules/library/library.v')
|
||||
assert os.is_file(library_file_path)
|
||||
result := v_compile('run use_shared_library.v')
|
||||
assert result.output.contains('res: 4')
|
||||
os.rm(library_file_path) or {}
|
||||
}
|
||||
|
||||
fn test_can_compile_and_use_library_with_skip_unused_location1_dir() {
|
||||
os.chdir(cfolder) or {}
|
||||
library_file_path := os.join_path(cfolder, 'location1', dl.get_libname('library'))
|
||||
os.rm(library_file_path) or {}
|
||||
os.mkdir('location1') or {}
|
||||
v_compile('-skip-unused -d no_backtrace -o location1/library -shared modules/library/library.v')
|
||||
assert os.is_file(library_file_path)
|
||||
result := v_compile('run use_shared_library.v')
|
||||
assert result.output.contains('res: 4')
|
||||
os.rm(library_file_path) or {}
|
||||
}
|
||||
|
||||
fn v_compile(vopts string) os.Result {
|
||||
cmd := '${os.quoted_path(vexe)} -showcc ${vopts}'
|
||||
// dump(cmd)
|
||||
res := os.execute_or_exit(cmd)
|
||||
// dump(res)
|
||||
assert res.exit_code == 0
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
module library
|
||||
|
||||
// add_1 is exported with the C name `add_1`.
|
||||
// It can be called by external programs, when the module is compiled
|
||||
// as a shared library.
|
||||
// It is exported, because the function is declared as public with `pub`.
|
||||
// The exported C name is `add_1`, because of the export: tag.
|
||||
// (Normally, the exported name is a V mangled version based on the module
|
||||
// name followed by __, followed by the fn name, i.e. it would have been
|
||||
// `library__add_1`, if not for the export: tag).
|
||||
@[export: 'add_1']
|
||||
pub fn add_1(x int, y int) int {
|
||||
return my_private_function(x + y)
|
||||
}
|
||||
|
||||
// this function is not exported and will not be visible to external programs.
|
||||
fn my_private_function(x int) int {
|
||||
return 1 + x
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
module main
|
||||
|
||||
// Note: This program, requires that the shared library was already compiled.
|
||||
// To do so, run `v -d no_backtrace -o library -shared modules/library/library.v`
|
||||
// before running this program.
|
||||
import os
|
||||
import dl
|
||||
|
||||
type FNAdder = fn (int, int) int
|
||||
|
||||
fn main() {
|
||||
library_file_path := os.join_path(os.dir(@FILE), dl.get_libname('library'))
|
||||
handle := dl.open_opt(library_file_path, dl.rtld_lazy)!
|
||||
eprintln('handle: ${ptr_str(handle)}')
|
||||
f := FNAdder(dl.sym_opt(handle, 'add_1')!)
|
||||
eprintln('f: ${ptr_str(f)}')
|
||||
res := f(1, 2)
|
||||
eprintln('res: ${res}')
|
||||
}
|
||||
@@ -0,0 +1,65 @@
|
||||
module main
|
||||
|
||||
// vtest retry: 3
|
||||
import os
|
||||
import dl
|
||||
|
||||
const vexe = os.real_path(os.getenv('VEXE'))
|
||||
const cfolder = os.dir(@FILE)
|
||||
const so_ext = dl.dl_ext
|
||||
const library_file_name = os.join_path(cfolder, dl.get_libname('library'))
|
||||
|
||||
fn test_vexe() {
|
||||
// dump(vexe)
|
||||
assert vexe != ''
|
||||
// dump(os.executable())
|
||||
// dump(@FILE)
|
||||
// dump(cfolder)
|
||||
// dump(so_ext)
|
||||
// dump(library_file_name)
|
||||
}
|
||||
|
||||
fn test_can_compile_library() {
|
||||
os.chdir(cfolder) or {}
|
||||
os.rm(library_file_name) or {}
|
||||
v_compile('-d no_backtrace -o library -shared modules/library/library.v')
|
||||
assert os.is_file(library_file_name)
|
||||
}
|
||||
|
||||
fn test_can_compile_main_program() {
|
||||
os.chdir(cfolder) or {}
|
||||
assert os.is_file(library_file_name)
|
||||
result := v_compile('run use_dl_module.v')
|
||||
// dump(result)
|
||||
assert result.output.contains('res: 4')
|
||||
os.rm(library_file_name) or {}
|
||||
}
|
||||
|
||||
fn test_can_compile_and_use_library_with_skip_unused() {
|
||||
os.chdir(cfolder) or {}
|
||||
os.rm(library_file_name) or {}
|
||||
v_compile('-skip-unused -d no_backtrace -o library -shared modules/library/library.v')
|
||||
assert os.is_file(library_file_name)
|
||||
result := v_compile('run use_dl_module.v')
|
||||
assert result.output.contains('res: 4')
|
||||
os.rm(library_file_name) or {}
|
||||
}
|
||||
|
||||
fn test_can_compile_and_use_library_with_prod() {
|
||||
os.chdir(cfolder) or {}
|
||||
os.rm(library_file_name) or {}
|
||||
v_compile('-prod -d no_backtrace -o library -shared modules/library/library.v')
|
||||
assert os.is_file(library_file_name)
|
||||
result := v_compile('run use_dl_module.v')
|
||||
assert result.output.contains('res: 4')
|
||||
os.rm(library_file_name) or {}
|
||||
}
|
||||
|
||||
fn v_compile(vopts string) os.Result {
|
||||
cmd := '${os.quoted_path(vexe)} -showcc ${vopts}'
|
||||
// dump(cmd)
|
||||
res := os.execute_or_exit(cmd)
|
||||
// dump(res)
|
||||
assert res.exit_code == 0
|
||||
return res
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
import semver
|
||||
|
||||
fn main() {
|
||||
semver.from('asd') or { check_error(err) }
|
||||
semver.from('') or { check_error(err) }
|
||||
}
|
||||
|
||||
fn check_error(err IError) {
|
||||
match err {
|
||||
semver.InvalidVersionFormatError {
|
||||
println('wrong format')
|
||||
}
|
||||
semver.EmptyInputError {
|
||||
println('empty input')
|
||||
}
|
||||
else {
|
||||
println('unknown error')
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
// This example shows 2 solutions for https://projecteuler.net/problem=1 :
|
||||
fn gauss_sum(n int) fn (int) int {
|
||||
return fn [n] (m int) int {
|
||||
return m * ((n - 1) / m) * ((n - 1) / m + 1) / 2
|
||||
}
|
||||
}
|
||||
|
||||
gs := gauss_sum(1000)
|
||||
println('O(1) arithmetic progression sum: ${gs(3) + gs(5) - gs(15)}')
|
||||
|
||||
// A brute force solution, by checking every n in the range:
|
||||
mut sum := 0
|
||||
for n in 1 .. 1000 {
|
||||
if n % 3 == 0 || n % 5 == 0 {
|
||||
sum += n
|
||||
}
|
||||
}
|
||||
println('O(n) brute force calculated sum: ${sum}')
|
||||
@@ -0,0 +1,33 @@
|
||||
module main
|
||||
|
||||
import some_module
|
||||
|
||||
struct Receiver {
|
||||
mut:
|
||||
ok bool
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut sub := some_module.get_subscriber()
|
||||
r := Receiver{}
|
||||
sub.subscribe_method('event_foo', on_foo, r)
|
||||
sub.subscribe('event_bar', on_bar)
|
||||
sub.subscribe('event_baz', on_baz)
|
||||
|
||||
println('Receiver ok: ' + r.ok.str())
|
||||
some_module.do_work()
|
||||
println('Receiver ok: ' + r.ok.str())
|
||||
}
|
||||
|
||||
fn on_foo(mut receiver Receiver, e &some_module.EventMetadata, _sender voidptr) {
|
||||
receiver.ok = true
|
||||
println('on_foo :: ' + e.message)
|
||||
}
|
||||
|
||||
fn on_bar(_receiver voidptr, e &some_module.EventMetadata, _sender voidptr) {
|
||||
println('on_bar :: ' + e.message)
|
||||
}
|
||||
|
||||
fn on_baz(_receiver voidptr, _event voidptr, d &some_module.Duration) {
|
||||
println('on_baz :: ' + d.hours.str())
|
||||
}
|
||||
@@ -0,0 +1,32 @@
|
||||
module some_module
|
||||
|
||||
import eventbus
|
||||
|
||||
const eb = eventbus.new[string]()
|
||||
|
||||
pub struct Duration {
|
||||
pub:
|
||||
hours int
|
||||
}
|
||||
|
||||
pub struct EventMetadata {
|
||||
pub:
|
||||
message string
|
||||
}
|
||||
|
||||
pub fn do_work() {
|
||||
duration := Duration{10}
|
||||
for i in 0 .. 10 {
|
||||
println('working...')
|
||||
if i == 5 {
|
||||
event_metadata := &EventMetadata{'Iteration ' + i.str()}
|
||||
eb.publish('event_foo', duration, event_metadata)
|
||||
eb.publish('event_bar', duration, event_metadata)
|
||||
}
|
||||
}
|
||||
eb.publish('event_baz', &Duration{42}, &EventMetadata{'Additional data at the end.'})
|
||||
}
|
||||
|
||||
pub fn get_subscriber() eventbus.Subscriber[string] {
|
||||
return *eb.subscriber
|
||||
}
|
||||
@@ -0,0 +1,71 @@
|
||||
# Fasthttp Example
|
||||
|
||||
A simple HTTP server example using the `fasthttp` module from `vlib/fasthttp`.
|
||||
|
||||
## Features
|
||||
|
||||
- Handles GET and POST requests
|
||||
- Routes requests to different controllers based on HTTP method and path
|
||||
- Returns appropriate HTTP responses with status codes and content
|
||||
|
||||
## Building
|
||||
|
||||
```sh
|
||||
./v examples/fasthttp
|
||||
```
|
||||
|
||||
## Running
|
||||
|
||||
```sh
|
||||
./examples/fasthttp/fasthttp
|
||||
```
|
||||
|
||||
The server will listen on `http://localhost:3000`
|
||||
|
||||
## Testing
|
||||
|
||||
### Home endpoint
|
||||
|
||||
```sh
|
||||
curl http://localhost:3000/
|
||||
```
|
||||
|
||||
### Get user by ID
|
||||
|
||||
```sh
|
||||
curl http://localhost:3000/user/123
|
||||
```
|
||||
|
||||
### Create user
|
||||
|
||||
```sh
|
||||
curl -X POST http://localhost:3000/user
|
||||
```
|
||||
|
||||
### 404 response
|
||||
|
||||
```sh
|
||||
curl http://localhost:3000/notfound
|
||||
```
|
||||
|
||||
## File Structure
|
||||
|
||||
- `main.v` - Entry point and request router
|
||||
- `controllers.v` - Request handlers for different routes
|
||||
- `v.mod` - Module metadata
|
||||
|
||||
## Architecture
|
||||
|
||||
The example demonstrates:
|
||||
|
||||
1. **Request Routing**: The `handle_request()` function routes incoming HTTP requests based on
|
||||
method and path
|
||||
2. **Response Handling**: Controllers return HTTP responses with proper headers and status codes
|
||||
3. **Content Type**: All responses are returned as `[]u8` (byte arrays)
|
||||
|
||||
The fasthttp module handles:
|
||||
|
||||
- Low-level socket management
|
||||
- Request parsing
|
||||
- Connection handling
|
||||
- Non-blocking I/O with epoll (Linux) or kqueue (macOS)
|
||||
@@ -0,0 +1,27 @@
|
||||
module main
|
||||
|
||||
fn home_controller() ![]u8 {
|
||||
response := 'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: 13\r\n\r\nHello, World!'
|
||||
return response.bytes()
|
||||
}
|
||||
|
||||
fn get_user_controller(id string) ![]u8 {
|
||||
body := 'User ID: ${id}'
|
||||
content_length := body.len
|
||||
response := 'HTTP/1.1 200 OK\r\nContent-Type: text/plain\r\nContent-Length: ${content_length}\r\n\r\n${body}'
|
||||
return response.bytes()
|
||||
}
|
||||
|
||||
fn create_user_controller() ![]u8 {
|
||||
body := 'User created successfully'
|
||||
content_length := body.len
|
||||
response := 'HTTP/1.1 201 Created\r\nContent-Type: text/plain\r\nContent-Length: ${content_length}\r\n\r\n${body}'
|
||||
return response.bytes()
|
||||
}
|
||||
|
||||
fn not_found_response() ![]u8 {
|
||||
body := '404 Not Found'
|
||||
content_length := body.len
|
||||
response := 'HTTP/1.1 404 Not Found\r\nContent-Type: text/plain\r\nContent-Length: ${content_length}\r\n\r\n${body}'
|
||||
return response.bytes()
|
||||
}
|
||||
@@ -0,0 +1,45 @@
|
||||
module main
|
||||
|
||||
import fasthttp
|
||||
|
||||
fn handle_request(req fasthttp.HttpRequest) !fasthttp.HttpResponse {
|
||||
method := req.buffer[req.method.start..req.method.start + req.method.len].bytestr()
|
||||
path := req.buffer[req.path.start..req.path.start + req.path.len].bytestr()
|
||||
|
||||
if method == 'GET' {
|
||||
if path == '/' {
|
||||
return fasthttp.HttpResponse{
|
||||
content: home_controller()!
|
||||
}
|
||||
} else if path.starts_with('/user/') {
|
||||
id := path[6..]
|
||||
return fasthttp.HttpResponse{
|
||||
content: get_user_controller(id)!
|
||||
}
|
||||
}
|
||||
} else if method == 'POST' {
|
||||
if path == '/user' {
|
||||
return fasthttp.HttpResponse{
|
||||
content: create_user_controller()!
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return fasthttp.HttpResponse{
|
||||
content: not_found_response()!
|
||||
}
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut server := fasthttp.new_server(fasthttp.ServerConfig{
|
||||
port: 3000
|
||||
handler: handle_request
|
||||
}) or {
|
||||
eprintln('Failed to create server: ${err}')
|
||||
return
|
||||
}
|
||||
|
||||
println('Starting fasthttp server on port http://localhost:3000...')
|
||||
|
||||
server.run() or { eprintln('error: ${err}') }
|
||||
}
|
||||
@@ -0,0 +1,6 @@
|
||||
Module {
|
||||
name: 'fasthttp_example'
|
||||
description: 'A simple HTTP server example using the fasthttp module'
|
||||
version: '0.0.1'
|
||||
license: 'MIT'
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
import time
|
||||
import net.http
|
||||
|
||||
fn main() {
|
||||
resp := http.get('https://vlang.io/utc_now') or {
|
||||
eprintln('Failed to fetch data from the server. Error: ${err}')
|
||||
return
|
||||
}
|
||||
|
||||
t := time.unix(resp.body.int())
|
||||
println(t.format())
|
||||
}
|
||||
@@ -0,0 +1,41 @@
|
||||
import json
|
||||
import net.http
|
||||
|
||||
struct IpInfo {
|
||||
ip string
|
||||
ip_decimal int
|
||||
country string
|
||||
country_iso string
|
||||
country_eu bool
|
||||
region_name string
|
||||
region_code string
|
||||
zip_code string
|
||||
city string
|
||||
latitude f64
|
||||
longitude f64
|
||||
time_zone string
|
||||
asn string
|
||||
asn_org string
|
||||
user_agent UserAgent
|
||||
}
|
||||
|
||||
struct UserAgent {
|
||||
product string
|
||||
version string
|
||||
comment string
|
||||
raw_value string
|
||||
}
|
||||
|
||||
url := 'http://ifconfig.co/json'
|
||||
resp := http.fetch(
|
||||
url: url
|
||||
user_agent: 'Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/117.0'
|
||||
) or {
|
||||
eprintln('failed to fetch data from the server, error: ${err}')
|
||||
exit(1)
|
||||
}
|
||||
ip_info := json.decode(IpInfo, resp.body) or {
|
||||
println('failed to decode the json data returned by the server, error: ${err}')
|
||||
exit(2)
|
||||
}
|
||||
println(ip_info)
|
||||
@@ -0,0 +1,31 @@
|
||||
// This program displays the fibonacci sequence
|
||||
const args = arguments()
|
||||
|
||||
fn main() {
|
||||
// Check for user input
|
||||
if args.len != 2 {
|
||||
println('usage: fibonacci [rank]')
|
||||
return
|
||||
}
|
||||
// Parse first argument and cast it to int
|
||||
stop := args[1].int()
|
||||
// Can only calculate correctly until rank 92
|
||||
if stop > 92 {
|
||||
println('rank must be 92 or less')
|
||||
return
|
||||
}
|
||||
// Three consecutive terms of the sequence
|
||||
mut a := i64(0)
|
||||
mut b := i64(0)
|
||||
mut c := i64(1)
|
||||
println(a + b + c)
|
||||
for _ in 0 .. stop {
|
||||
// Set a and b to the next term
|
||||
a = b
|
||||
b = c
|
||||
// Compute the new term
|
||||
c = a + b
|
||||
// Print the new term
|
||||
println(c)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
import os
|
||||
|
||||
fn main() {
|
||||
files := os.ls('.') or {
|
||||
println(err)
|
||||
return
|
||||
}
|
||||
mut f := os.create('file_list.txt') or {
|
||||
println(err)
|
||||
return
|
||||
}
|
||||
for file in files {
|
||||
if os.is_file(file) {
|
||||
f.write_string(file + '\r\n') or { println(err) }
|
||||
}
|
||||
}
|
||||
f.close()
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
import objects
|
||||
import gg
|
||||
import rand
|
||||
|
||||
struct App {
|
||||
mut:
|
||||
gg &gg.Context = unsafe { nil }
|
||||
ui &objects.UIParams = unsafe { nil }
|
||||
rockets []objects.Rocket
|
||||
frames [][]objects.Rocket
|
||||
// i thought about using a fixed fifo queue for the frames but the array
|
||||
// seemed to work fine, if you'd like a challenge try implementing it with the queue :)
|
||||
draw_flag bool = true
|
||||
}
|
||||
|
||||
fn on_frame(mut app App) {
|
||||
if !app.draw_flag {
|
||||
return
|
||||
}
|
||||
app.gg.begin()
|
||||
|
||||
// drawing previous frames
|
||||
for mut frame in app.frames {
|
||||
for mut rocket in frame {
|
||||
if !rocket.exploded {
|
||||
rocket.color.a = u8(f32_max(rocket.color.a - 8, 0))
|
||||
rocket.draw(mut app.gg)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// chance of firing new rocket
|
||||
if rand.intn(30) or { 0 } == 0 {
|
||||
app.rockets << objects.new_rocket()
|
||||
}
|
||||
// simulating rockets
|
||||
app.rockets = app.rockets.filter(!it.dead)
|
||||
|
||||
for mut rocket in app.rockets {
|
||||
rocket.tick(mut app.gg)
|
||||
}
|
||||
|
||||
// adding frame
|
||||
mut frame := app.rockets.clone()
|
||||
|
||||
for mut rocket in frame {
|
||||
rocket.particles = []
|
||||
}
|
||||
|
||||
app.frames << frame
|
||||
|
||||
// trimming out frames
|
||||
if app.frames.len > 30 {
|
||||
app.frames.delete(0)
|
||||
}
|
||||
|
||||
app.gg.end()
|
||||
}
|
||||
|
||||
fn on_event(e &gg.Event, mut app App) {
|
||||
match e.typ {
|
||||
.resized, .resumed {
|
||||
app.resize()
|
||||
}
|
||||
.iconified {
|
||||
app.draw_flag = false
|
||||
}
|
||||
.restored {
|
||||
app.draw_flag = true
|
||||
app.resize()
|
||||
}
|
||||
else {
|
||||
// println("Type ${e.typ}")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fn (mut app App) resize() {
|
||||
size := gg.window_size()
|
||||
// avoid calls when minimized
|
||||
if size.width < 2 && size.height < 2 {
|
||||
return
|
||||
}
|
||||
mut s := gg.dpi_scale()
|
||||
|
||||
if s == 0.0 {
|
||||
s = 1.0
|
||||
}
|
||||
app.ui.dpi_scale = s
|
||||
app.ui.width = size.width
|
||||
app.ui.height = size.height
|
||||
}
|
||||
|
||||
fn main() {
|
||||
mut app := &App{}
|
||||
app.ui = objects.get_params()
|
||||
app.gg = gg.new_context(
|
||||
width: app.ui.width
|
||||
height: app.ui.height
|
||||
window_title: 'Fireworks!'
|
||||
bg_color: gg.black
|
||||
user_data: app
|
||||
frame_fn: on_frame
|
||||
event_fn: on_event
|
||||
)
|
||||
app.gg.run()
|
||||
}
|
||||
@@ -0,0 +1,12 @@
|
||||
module objects
|
||||
|
||||
import gg
|
||||
import rand
|
||||
|
||||
pub fn random_color() gg.Color {
|
||||
return gg.Color{
|
||||
r: rand.u8()
|
||||
g: rand.u8()
|
||||
b: rand.u8()
|
||||
}
|
||||
}
|
||||
Loaded 100 of 11957 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user