js,os: fix integer arithmetic; add Process.close() and os.is_readable() for VJS; Make tetris run on JS backend (#12940)
parent
d3ccdfd75c
commit
a2468e1e0c
|
@ -7,3 +7,11 @@ sudo apt install libxi-dev
|
||||||
sudo apt install libxcursor-dev
|
sudo apt install libxcursor-dev
|
||||||
sudo apt install libgl-dev
|
sudo apt install libgl-dev
|
||||||
```
|
```
|
||||||
|
|
||||||
|
## Compiling to JS
|
||||||
|
|
||||||
|
```sh
|
||||||
|
v -b js_browser examples/tetris/tetris.js.v
|
||||||
|
```
|
||||||
|
|
||||||
|
And then open `index.html` with your favourite web browser.
|
|
@ -0,0 +1,4 @@
|
||||||
|
<body class="main">
|
||||||
|
<canvas width="200" height="400" id="canvas" style="border: 1px solid black;"></canvas>
|
||||||
|
<script src="tetris.js"></script>
|
||||||
|
</body>
|
|
@ -0,0 +1,497 @@
|
||||||
|
// Copyright (c) 2019-2021 Alexander Medvednikov. All rights reserved.
|
||||||
|
// Use of this source code is governed by an MIT license
|
||||||
|
// that can be found in the LICENSE file.
|
||||||
|
module main
|
||||||
|
|
||||||
|
import rand
|
||||||
|
import time
|
||||||
|
import gx
|
||||||
|
import gg
|
||||||
|
// import sokol.sapp
|
||||||
|
|
||||||
|
const (
|
||||||
|
block_size = 20 // virtual pixels
|
||||||
|
field_height = 20 // # of blocks
|
||||||
|
field_width = 10
|
||||||
|
tetro_size = 4
|
||||||
|
win_width = block_size * field_width
|
||||||
|
win_height = block_size * field_height
|
||||||
|
timer_period = 250 // ms
|
||||||
|
text_size = 24
|
||||||
|
limit_thickness = 3
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
text_cfg = gx.TextCfg{
|
||||||
|
align: .left
|
||||||
|
size: text_size
|
||||||
|
color: gx.rgb(0, 0, 0)
|
||||||
|
}
|
||||||
|
over_cfg = gx.TextCfg{
|
||||||
|
align: .left
|
||||||
|
size: text_size
|
||||||
|
color: gx.white
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
const (
|
||||||
|
// Tetros' 4 possible states are encoded in binaries
|
||||||
|
// 0000 0 0000 0 0000 0 0000 0 0000 0 0000 0
|
||||||
|
// 0000 0 0000 0 0000 0 0000 0 0011 3 0011 3
|
||||||
|
// 0110 6 0010 2 0011 3 0110 6 0001 1 0010 2
|
||||||
|
// 0110 6 0111 7 0110 6 0011 3 0001 1 0010 2
|
||||||
|
// There is a special case 1111, since 15 can't be used.
|
||||||
|
b_tetros = [
|
||||||
|
[66, 66, 66, 66],
|
||||||
|
[27, 131, 72, 232],
|
||||||
|
[36, 231, 36, 231],
|
||||||
|
[63, 132, 63, 132],
|
||||||
|
[311, 17, 223, 74],
|
||||||
|
[322, 71, 113, 47],
|
||||||
|
[1111, 9, 1111, 9],
|
||||||
|
]
|
||||||
|
// Each tetro has its unique color
|
||||||
|
colors = [
|
||||||
|
gx.rgb(0, 0, 0), /* unused ? */
|
||||||
|
gx.rgb(255, 242, 0), /* yellow quad */
|
||||||
|
gx.rgb(174, 0, 255), /* purple triple */
|
||||||
|
gx.rgb(60, 255, 0), /* green short topright */
|
||||||
|
gx.rgb(255, 0, 0), /* red short topleft */
|
||||||
|
gx.rgb(255, 180, 31), /* orange long topleft */
|
||||||
|
gx.rgb(33, 66, 255), /* blue long topright */
|
||||||
|
gx.rgb(74, 198, 255), /* lightblue longest */
|
||||||
|
gx.rgb(0, 170, 170),
|
||||||
|
]
|
||||||
|
background_color = gx.white
|
||||||
|
ui_color = gx.rgba(255, 0, 0, 210)
|
||||||
|
)
|
||||||
|
|
||||||
|
// TODO: type Tetro [tetro_size]struct{ x, y int }
|
||||||
|
struct Block {
|
||||||
|
mut:
|
||||||
|
x int
|
||||||
|
y int
|
||||||
|
}
|
||||||
|
|
||||||
|
enum GameState {
|
||||||
|
paused
|
||||||
|
running
|
||||||
|
gameover
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Game {
|
||||||
|
mut:
|
||||||
|
// Score of the current game
|
||||||
|
score int
|
||||||
|
// Lines of the current game
|
||||||
|
lines int
|
||||||
|
// State of the current game
|
||||||
|
state GameState
|
||||||
|
// Block size in screen dimensions
|
||||||
|
block_size int = block_size
|
||||||
|
// Field margin
|
||||||
|
margin int
|
||||||
|
// Position of the current tetro
|
||||||
|
pos_x int
|
||||||
|
pos_y int
|
||||||
|
// field[y][x] contains the color of the block with (x,y) coordinates
|
||||||
|
// "-1" border is to avoid bounds checking.
|
||||||
|
// -1 -1 -1 -1
|
||||||
|
// -1 0 0 -1
|
||||||
|
// -1 0 0 -1
|
||||||
|
// -1 -1 -1 -1
|
||||||
|
field [][]int
|
||||||
|
// TODO: tetro Tetro
|
||||||
|
tetro []Block
|
||||||
|
// TODO: tetros_cache []Tetro
|
||||||
|
tetros_cache []Block
|
||||||
|
// Index of the current tetro. Refers to its color.
|
||||||
|
tetro_idx int
|
||||||
|
// Idem for the next tetro
|
||||||
|
next_tetro_idx int
|
||||||
|
// Index of the rotation (0-3)
|
||||||
|
rotation_idx int
|
||||||
|
// gg context for drawing
|
||||||
|
gg &gg.Context = voidptr(0)
|
||||||
|
font_loaded bool
|
||||||
|
show_ghost bool = true
|
||||||
|
// frame/time counters:
|
||||||
|
frame int
|
||||||
|
frame_old int
|
||||||
|
frame_sw time.StopWatch = time.new_stopwatch()
|
||||||
|
second_sw time.StopWatch = time.new_stopwatch()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn remap(v f32, min f32, max f32, new_min f32, new_max f32) f32 {
|
||||||
|
return (((v - min) * (new_max - new_min)) / (max - min)) + new_min
|
||||||
|
}
|
||||||
|
|
||||||
|
[if showfps ?]
|
||||||
|
fn (mut game Game) showfps() {
|
||||||
|
game.frame++
|
||||||
|
last_frame_ms := f64(game.frame_sw.elapsed().microseconds()) / 1000.0
|
||||||
|
ticks := f64(game.second_sw.elapsed().microseconds()) / 1000.0
|
||||||
|
if ticks > 999.0 {
|
||||||
|
fps := f64(game.frame - game.frame_old) * ticks / 1000.0
|
||||||
|
$if debug {
|
||||||
|
eprintln('fps: ${fps:5.1f} | last frame took: ${last_frame_ms:6.3f}ms | frame: ${game.frame:6} ')
|
||||||
|
}
|
||||||
|
game.second_sw.restart()
|
||||||
|
game.frame_old = game.frame
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn frame(mut game Game) {
|
||||||
|
if game.gg.frame & 15 == 0 {
|
||||||
|
game.update_game_state()
|
||||||
|
}
|
||||||
|
ws := gg.window_size()
|
||||||
|
bs := remap(block_size, 0, win_height, 0, ws.height)
|
||||||
|
m := (f32(ws.width) - bs * field_width) * 0.5
|
||||||
|
game.block_size = int(bs)
|
||||||
|
game.margin = int(m)
|
||||||
|
game.frame_sw.restart()
|
||||||
|
game.gg.begin()
|
||||||
|
game.draw_scene()
|
||||||
|
game.showfps()
|
||||||
|
game.gg.end()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn main() {
|
||||||
|
mut game := &Game{
|
||||||
|
gg: &gg.Context{}
|
||||||
|
}
|
||||||
|
|
||||||
|
game.gg = gg.new_context(
|
||||||
|
bg_color: gx.white
|
||||||
|
width: win_width
|
||||||
|
height: win_height
|
||||||
|
create_window: true
|
||||||
|
window_title: 'V Tetris' //
|
||||||
|
user_data: game
|
||||||
|
frame_fn: frame
|
||||||
|
event_fn: on_event
|
||||||
|
canvas: 'canvas'
|
||||||
|
)
|
||||||
|
game.init_game()
|
||||||
|
game.gg.run() // Run the render loop in the main thread
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) init_game() {
|
||||||
|
g.parse_tetros()
|
||||||
|
g.next_tetro_idx = rand.intn(b_tetros.len) // generate initial "next"
|
||||||
|
g.generate_tetro()
|
||||||
|
g.field = []
|
||||||
|
// Generate the field, fill it with 0's, add -1's on each edge
|
||||||
|
for _ in 0 .. field_height + 2 {
|
||||||
|
mut row := [0].repeat(field_width + 2)
|
||||||
|
row[0] = -1
|
||||||
|
row[field_width + 1] = -1
|
||||||
|
g.field << row
|
||||||
|
}
|
||||||
|
for j in 0 .. field_width + 2 {
|
||||||
|
g.field[0][j] = -1
|
||||||
|
g.field[field_height + 1][j] = -1
|
||||||
|
}
|
||||||
|
g.score = 0
|
||||||
|
g.lines = 0
|
||||||
|
g.state = .running
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) parse_tetros() {
|
||||||
|
for b_tetros0 in b_tetros {
|
||||||
|
for b_tetro in b_tetros0 {
|
||||||
|
for t in parse_binary_tetro(b_tetro) {
|
||||||
|
g.tetros_cache << t
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) update_game_state() {
|
||||||
|
if g.state == .running {
|
||||||
|
g.move_tetro()
|
||||||
|
g.delete_completed_lines()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) draw_ghost() {
|
||||||
|
if g.state != .gameover && g.show_ghost {
|
||||||
|
pos_y := g.move_ghost()
|
||||||
|
for i in 0 .. tetro_size {
|
||||||
|
tetro := g.tetro[i]
|
||||||
|
g.draw_block_color(pos_y + tetro.y, g.pos_x + tetro.x, gx.rgba(125, 125, 225,
|
||||||
|
40))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (g Game) move_ghost() int {
|
||||||
|
mut pos_y := g.pos_y
|
||||||
|
mut end := false
|
||||||
|
for !end {
|
||||||
|
for block in g.tetro {
|
||||||
|
y := block.y + pos_y + 1
|
||||||
|
x := block.x + g.pos_x
|
||||||
|
if g.field[y][x] != 0 {
|
||||||
|
end = true
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pos_y++
|
||||||
|
}
|
||||||
|
return pos_y - 1
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) move_tetro() bool {
|
||||||
|
// Check each block in current tetro
|
||||||
|
for block in g.tetro {
|
||||||
|
y := block.y + g.pos_y + 1
|
||||||
|
x := block.x + g.pos_x
|
||||||
|
// Reached the bottom of the screen or another block?
|
||||||
|
if g.field[y][x] != 0 {
|
||||||
|
// The new tetro has no space to drop => end of the game
|
||||||
|
if g.pos_y < 2 {
|
||||||
|
g.state = .gameover
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
// Drop it and generate a new one
|
||||||
|
g.drop_tetro()
|
||||||
|
g.generate_tetro()
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.pos_y++
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) move_right(dx int) bool {
|
||||||
|
// Reached left/right edge or another tetro?
|
||||||
|
for i in 0 .. tetro_size {
|
||||||
|
tetro := g.tetro[i]
|
||||||
|
y := tetro.y + g.pos_y
|
||||||
|
x := tetro.x + g.pos_x + dx
|
||||||
|
if g.field[y][x] != 0 {
|
||||||
|
// Do not move
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.pos_x += dx
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) delete_completed_lines() {
|
||||||
|
for y := field_height; y >= 1; y-- {
|
||||||
|
g.delete_completed_line(y)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) delete_completed_line(y int) {
|
||||||
|
for x := 1; x <= field_width; x++ {
|
||||||
|
if g.field[y][x] == 0 {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
g.score += 10
|
||||||
|
g.lines++
|
||||||
|
// Move everything down by 1 position
|
||||||
|
for yy := y - 1; yy >= 1; yy-- {
|
||||||
|
for x := 1; x <= field_width; x++ {
|
||||||
|
g.field[yy + 1][x] = g.field[yy][x]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Place a new tetro on top
|
||||||
|
fn (mut g Game) generate_tetro() {
|
||||||
|
g.pos_y = 0
|
||||||
|
g.pos_x = field_width / 2 - tetro_size / 2
|
||||||
|
g.tetro_idx = g.next_tetro_idx
|
||||||
|
g.next_tetro_idx = rand.intn(b_tetros.len)
|
||||||
|
g.rotation_idx = 0
|
||||||
|
g.get_tetro()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get the right tetro from cache
|
||||||
|
fn (mut g Game) get_tetro() {
|
||||||
|
idx := g.tetro_idx * tetro_size * tetro_size + g.rotation_idx * tetro_size
|
||||||
|
mut tetros := []Block{}
|
||||||
|
for tetro in g.tetros_cache[idx..idx + tetro_size] {
|
||||||
|
tetros << Block{tetro.x, tetro.y}
|
||||||
|
}
|
||||||
|
g.tetro = tetros
|
||||||
|
// g.tetro = g.tetros_cache[idx..idx + tetro_size].clone()
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO mut
|
||||||
|
fn (mut g Game) drop_tetro() {
|
||||||
|
for i in 0 .. tetro_size {
|
||||||
|
tetro := g.tetro[i]
|
||||||
|
x := tetro.x + g.pos_x
|
||||||
|
y := tetro.y + g.pos_y
|
||||||
|
// Remember the color of each block
|
||||||
|
g.field[y][x] = g.tetro_idx + 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) draw_tetro() {
|
||||||
|
for i in 0 .. tetro_size {
|
||||||
|
tetro := g.tetro[i]
|
||||||
|
g.draw_block(g.pos_y + tetro.y, g.pos_x + tetro.x, g.tetro_idx + 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) draw_next_tetro() {
|
||||||
|
if g.state != .gameover {
|
||||||
|
idx := g.next_tetro_idx * tetro_size * tetro_size
|
||||||
|
next_tetro := g.tetros_cache[idx..idx + tetro_size]
|
||||||
|
pos_y := 0
|
||||||
|
pos_x := field_width / 2 - tetro_size / 2
|
||||||
|
for i in 0 .. tetro_size {
|
||||||
|
block := next_tetro[i]
|
||||||
|
g.draw_block_color(pos_y + block.y, pos_x + block.x, gx.rgb(220, 220, 220))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) draw_block_color(i int, j int, color gx.Color) {
|
||||||
|
g.gg.draw_rect(f32((j - 1) * g.block_size) + g.margin, f32((i - 1) * g.block_size),
|
||||||
|
f32(g.block_size - 1), f32(g.block_size - 1), color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) draw_block(i int, j int, color_idx int) {
|
||||||
|
color := if g.state == .gameover { gx.gray } else { colors[color_idx] }
|
||||||
|
g.draw_block_color(i, j, color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) draw_field() {
|
||||||
|
for i := 1; i < field_height + 1; i++ {
|
||||||
|
for j := 1; j < field_width + 1; j++ {
|
||||||
|
if g.field[i][j] > 0 {
|
||||||
|
g.draw_block(i, j, g.field[i][j])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) draw_ui() {
|
||||||
|
ws := gg.window_size()
|
||||||
|
textsize := int(remap(text_size, 0, win_width, 0, ws.width))
|
||||||
|
g.gg.draw_text(1, 10, g.score.str(), text_cfg)
|
||||||
|
lines := g.lines.str()
|
||||||
|
g.gg.draw_text(ws.width - lines.len * textsize, 10, lines, text_cfg)
|
||||||
|
if g.state == .gameover {
|
||||||
|
g.gg.draw_rect(0, ws.height / 2 - textsize, ws.width, 5 * textsize, ui_color)
|
||||||
|
g.gg.draw_text(1, ws.height / 2 + 0 * textsize, 'Game Over', over_cfg)
|
||||||
|
g.gg.draw_text(1, ws.height / 2 + 2 * textsize, 'Space to restart', over_cfg)
|
||||||
|
} else if g.state == .paused {
|
||||||
|
g.gg.draw_rect(0, ws.height / 2 - textsize, ws.width, 5 * textsize, ui_color)
|
||||||
|
g.gg.draw_text(1, ws.height / 2 + 0 * textsize, 'Game Paused', text_cfg)
|
||||||
|
g.gg.draw_text(1, ws.height / 2 + 2 * textsize, 'SPACE to resume', text_cfg)
|
||||||
|
}
|
||||||
|
// g.gg.draw_rect(0, block_size, win_width, limit_thickness, ui_color)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut g Game) draw_scene() {
|
||||||
|
g.draw_ghost()
|
||||||
|
g.draw_next_tetro()
|
||||||
|
g.draw_tetro()
|
||||||
|
g.draw_field()
|
||||||
|
g.draw_ui()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_binary_tetro(t_ int) []Block {
|
||||||
|
mut t := t_
|
||||||
|
mut res := [Block{}, Block{}, Block{}, Block{}]
|
||||||
|
mut cnt := 0
|
||||||
|
horizontal := t == 9 // special case for the horizontal line
|
||||||
|
ten_powers := [1000, 100, 10, 1]
|
||||||
|
for i := 0; i <= 3; i++ {
|
||||||
|
// Get ith digit of t
|
||||||
|
p := ten_powers[i]
|
||||||
|
mut digit := t / p
|
||||||
|
t %= p
|
||||||
|
|
||||||
|
// Convert the digit to binary
|
||||||
|
for j := 3; j >= 0; j-- {
|
||||||
|
bin := digit % 2
|
||||||
|
digit /= 2
|
||||||
|
if bin == 1 || (horizontal && i == tetro_size - 1) {
|
||||||
|
res[cnt].x = j
|
||||||
|
res[cnt].y = i
|
||||||
|
cnt++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return res
|
||||||
|
}
|
||||||
|
|
||||||
|
fn on_event(e &gg.Event, mut game Game) {
|
||||||
|
// println('code=$e.char_code')
|
||||||
|
if e.typ == .key_down {
|
||||||
|
game.key_down(e.key_code)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut game Game) rotate_tetro() {
|
||||||
|
old_rotation_idx := game.rotation_idx
|
||||||
|
game.rotation_idx++
|
||||||
|
if game.rotation_idx == tetro_size {
|
||||||
|
game.rotation_idx = 0
|
||||||
|
}
|
||||||
|
game.get_tetro()
|
||||||
|
if !game.move_right(0) {
|
||||||
|
game.rotation_idx = old_rotation_idx
|
||||||
|
game.get_tetro()
|
||||||
|
}
|
||||||
|
if game.pos_x < 0 {
|
||||||
|
// game.pos_x = 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn (mut game Game) key_down(key gg.KeyCode) {
|
||||||
|
// global keys
|
||||||
|
match key {
|
||||||
|
.escape {
|
||||||
|
game.gg.quit()
|
||||||
|
}
|
||||||
|
.space {
|
||||||
|
if game.state == .running {
|
||||||
|
game.state = .paused
|
||||||
|
} else if game.state == .paused {
|
||||||
|
game.state = .running
|
||||||
|
} else if game.state == .gameover {
|
||||||
|
game.init_game()
|
||||||
|
game.state = .running
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
if game.state != .running {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
// keys while game is running
|
||||||
|
match key {
|
||||||
|
.up {
|
||||||
|
// Rotate the tetro
|
||||||
|
game.rotate_tetro()
|
||||||
|
}
|
||||||
|
.left {
|
||||||
|
game.move_right(-1)
|
||||||
|
}
|
||||||
|
.right {
|
||||||
|
game.move_right(1)
|
||||||
|
}
|
||||||
|
.down {
|
||||||
|
game.move_tetro() // drop faster when the player presses <down>
|
||||||
|
}
|
||||||
|
.d {
|
||||||
|
for game.move_tetro() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.g {
|
||||||
|
game.show_ghost = !game.show_ghost
|
||||||
|
}
|
||||||
|
else {}
|
||||||
|
}
|
||||||
|
}
|
|
@ -140,3 +140,14 @@ pub fn execvp(cmd string, args []string) ? {
|
||||||
pub fn stdin_resume() {
|
pub fn stdin_resume() {
|
||||||
#$process.stdin.resume();
|
#$process.stdin.resume();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn is_readable(path string) bool {
|
||||||
|
$if js_node {
|
||||||
|
res := false
|
||||||
|
#try { res.val = $fs.accessSync(path.str,$fs.constants.R_OK); } catch { res.val = false; }
|
||||||
|
|
||||||
|
return res
|
||||||
|
} $else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -26,7 +26,6 @@ fn (mut p Process) spawn_internal() {
|
||||||
#p.val.pid.on('error', function (err) { builtin.panic('Failed to start subprocess') })
|
#p.val.pid.on('error', function (err) { builtin.panic('Failed to start subprocess') })
|
||||||
|
|
||||||
p.status = .running
|
p.status = .running
|
||||||
// todo(playX): stderr,stdin
|
|
||||||
if p.use_stdio_ctl {
|
if p.use_stdio_ctl {
|
||||||
#p.val.pid.stdout.pipe(process.stdout)
|
#p.val.pid.stdout.pipe(process.stdout)
|
||||||
#p.val.pid.stdin.pipe(process.stdin)
|
#p.val.pid.stdin.pipe(process.stdin)
|
||||||
|
@ -121,3 +120,7 @@ fn (mut p Process) check_redirection_call(fn_name string) {
|
||||||
panic('Call p.${fn_name}() after you have called p.run()')
|
panic('Call p.${fn_name}() after you have called p.run()')
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn (mut p Process) close() {
|
||||||
|
// no-op on JS backend
|
||||||
|
}
|
||||||
|
|
|
@ -342,7 +342,7 @@ fn (mut g JsGen) gen_builtin_type_defs() {
|
||||||
typ_name: typ_name
|
typ_name: typ_name
|
||||||
default_value: 'new Number(0)'
|
default_value: 'new Number(0)'
|
||||||
// mask <=32 bit numbers with 0xffffffff
|
// mask <=32 bit numbers with 0xffffffff
|
||||||
constructor: 'this.val = Math.round(Number(val)) & 0xffffffff'
|
constructor: 'this.val = Math.floor(Number(val) & 0xffffffff) '
|
||||||
value_of: 'Number(this.val)'
|
value_of: 'Number(this.val)'
|
||||||
to_string: 'this.valueOf().toString()'
|
to_string: 'this.valueOf().toString()'
|
||||||
eq: 'new bool(self.valueOf() === other.valueOf())'
|
eq: 'new bool(self.valueOf() === other.valueOf())'
|
||||||
|
|
|
@ -1327,7 +1327,7 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) {
|
||||||
is_ptr = true
|
is_ptr = true
|
||||||
g.write('.val')
|
g.write('.val')
|
||||||
}
|
}
|
||||||
|
mut floor := false
|
||||||
if false && g.inside_map_set && op == .assign {
|
if false && g.inside_map_set && op == .assign {
|
||||||
g.inside_map_set = false
|
g.inside_map_set = false
|
||||||
g.write('] = ')
|
g.write('] = ')
|
||||||
|
@ -1338,6 +1338,7 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) {
|
||||||
} else {
|
} else {
|
||||||
if is_assign && array_set {
|
if is_assign && array_set {
|
||||||
g.write('new ${styp}(')
|
g.write('new ${styp}(')
|
||||||
|
|
||||||
g.expr(left)
|
g.expr(left)
|
||||||
l_sym := g.table.sym(stmt.left_types[i])
|
l_sym := g.table.sym(stmt.left_types[i])
|
||||||
if l_sym.kind == .string {
|
if l_sym.kind == .string {
|
||||||
|
@ -1392,6 +1393,11 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) {
|
||||||
if !array_set {
|
if !array_set {
|
||||||
g.write(' = ')
|
g.write(' = ')
|
||||||
}
|
}
|
||||||
|
if (l_sym.name != 'f64' || l_sym.name != 'f32')
|
||||||
|
&& (l_sym.name != 'i64' && l_sym.name != 'u64') && l_sym.name != 'string' {
|
||||||
|
g.write('Math.floor(')
|
||||||
|
floor = true
|
||||||
|
}
|
||||||
g.expr(left)
|
g.expr(left)
|
||||||
|
|
||||||
match op {
|
match op {
|
||||||
|
@ -1460,6 +1466,9 @@ fn (mut g JsGen) gen_assign_stmt(stmt ast.AssignStmt, semicolon bool) {
|
||||||
if is_assign && array_set {
|
if is_assign && array_set {
|
||||||
g.write(')')
|
g.write(')')
|
||||||
}
|
}
|
||||||
|
if floor {
|
||||||
|
g.write(')')
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if array_set && !map_set {
|
if array_set && !map_set {
|
||||||
g.write(')')
|
g.write(')')
|
||||||
|
|
Loading…
Reference in New Issue