1
0
mirror of https://github.com/vlang/v.git synced 2023-08-10 21:13:21 +03:00

generics, vweb, comptime codegen, etc

This commit is contained in:
Alexander Medvednikov
2019-07-29 18:21:36 +02:00
parent f1373874ef
commit 207bab5f79
20 changed files with 982 additions and 442 deletions

View File

@@ -13,7 +13,7 @@ struct Option {
// `fn foo() ?Foo { return foo }` => `fn foo() ?Foo { return opt_ok(foo); }`
fn opt_ok(data voidptr, size int) Option {
if size > 255 {
panic('option size too big: $size (max is 255)')
panic('option size too big: $size (max is 255), this is a temporary limit')
}
res := Option {
ok: true

View File

@@ -296,4 +296,4 @@ pub fn (d &Digest) size() int {
}
}
pub fn (d &Digest) block_size() int { return BlockSize }
pub fn (d &Digest) block_size() int { return BlockSize }

View File

@@ -189,11 +189,11 @@ pub fn (req &Request) do() Response {
}
}
fn unescape(s string) string {
pub fn unescape(s string) string {
return string(byteptr(C.curl_unescape(s.str, s.len)))
}
fn escape(s string) string {
pub fn escape(s string) string {
return string(byteptr(C.curl_escape(s.str, s.len)))
}

View File

@@ -248,4 +248,4 @@ pub fn range(arr []f64) f64 {
return f64(0)
}
return max(arr) - min(arr)
}
}

View File

@@ -79,6 +79,10 @@ pub fn socket(family int, _type int, proto int) ?Socket {
}
sockfd := C.socket(family, _type, proto)
one:=1
// This is needed so that there are no problems with reusing the
// same port after the application exits.
C.setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(int))
if sockfd == 0 {
return error('socket: init failed')
}
@@ -117,11 +121,12 @@ pub fn (s Socket) bind(port int) ?int {
// put socket into passive mode and wait to receive
pub fn (s Socket) listen() ?int {
backlog := 128
res := C.listen(s.sockfd, backlog)
res := int(C.listen(s.sockfd, backlog))
if res < 0 {
return error('socket: listen failed')
}
return int(res)
println('liisten res = $res')
return res
}
// put socket into passive mode with user specified backlog and wait to receive
@@ -139,6 +144,7 @@ pub fn (s Socket) listen_backlog(backlog int) ?int {
// helper method to create, bind, and listen given port number
pub fn listen(port int) ?Socket {
println('net.listen($port)')
s := socket(AF_INET, SOCK_STREAM, 0) or {
return error(err)
}
@@ -153,6 +159,7 @@ pub fn listen(port int) ?Socket {
// accept first connection request from socket queue
pub fn (s Socket) accept() ?Socket {
println('accept()')
addr := C.sockaddr_storage{}
size := 128 // sizeof(sockaddr_storage)
sockfd := C.accept(s.sockfd, &addr, &size)
@@ -251,3 +258,47 @@ pub fn (s Socket) close() ?int {
return 0
}
const (
MAX_READ = 400
)
pub fn (s Socket) write(str string) {
line := '$str\r\n'
C.write(s.sockfd, line.str, line.len)
}
pub fn (s Socket) read_line() string {
mut res := ''
for {
println('.')
mut buf := malloc(MAX_READ)
n := int(C.recv(s.sockfd, buf, MAX_READ-1, 0))
println('numbytes=$n')
if n == -1 {
println('recv failed')
// TODO
return ''
}
if n == 0 {
break
}
// println('resp len=$numbytes')
buf[n] = `\0`
// C.printf('!!buf= "%s" n=%d\n', buf,n)
line := string(buf)
res += line
// Reached a newline. That's an end of an IRC message
// TODO dont need ends_with check ?
if line.ends_with('\n') || n < MAX_READ - 1 {
// println('NL')
break
}
if line.ends_with('\r\n') {
// println('RNL')
break
}
}
return res
}

View File

@@ -14,4 +14,4 @@ pub fn get_error_msg(code int) string {
return ''
}
return tos(_ptr_text, C.strlen(_ptr_text))
}
}

View File

@@ -90,4 +90,4 @@ pub fn get_error_msg(code int) string {
return ''
}
return tos(_ptr_text, C.strlen(_ptr_text))
}
}

View File

@@ -56,10 +56,110 @@ pub fn random() Time {
}
}
pub fn unix(u int) Time {
mut t := &C.tm{!}
t = C.localtime(&u)
return convert_ctime(t)
const (
// The unsigned zero year for internal calculations.
// Must be 1 mod 400, and times before it will not compute correctly,
// but otherwise can be changed at will.
absoluteZeroYear = i64(-292277022399)
secondsPerMinute = 60
secondsPerHour = 60 * secondsPerMinute
secondsPerDay = 24 * secondsPerHour
secondsPerWeek = 7 * secondsPerDay
daysPer400Years = 365*400 + 97
daysPer100Years = 365*100 + 24
daysPer4Years = 365*4 + 1
daysBefore = [
0,
31,
31 + 28,
31 + 28 + 31,
31 + 28 + 31 + 30,
31 + 28 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30,
31 + 28 + 31 + 30 + 31 + 30 + 31 + 31 + 30 + 31 + 30 + 31,
]
)
// Based on Go's time package.
// Copyright 2009 The Go Authors.
pub fn unix(abs int) Time {
// Split into time and day.
mut d := abs / secondsPerDay
// Account for 400 year cycles.
mut n := d / daysPer400Years
mut y := 400 * n
d -= daysPer400Years * n
// Cut off 100-year cycles.
// The last cycle has one extra leap year, so on the last day
// of that year, day / daysPer100Years will be 4 instead of 3.
// Cut it back down to 3 by subtracting n>>2.
n = d / daysPer100Years
n -= n >> 2
y += 100 * n
d -= daysPer100Years * n
// Cut off 4-year cycles.
// The last cycle has a missing leap year, which does not
// affect the computation.
n = d / daysPer4Years
y += 4 * n
d -= daysPer4Years * n
// Cut off years within a 4-year cycle.
// The last year is a leap year, so on the last day of that year,
// day / 365 will be 4 instead of 3. Cut it back down to 3
// by subtracting n>>2.
n = d / 365
n -= n >> 2
y += n
d -= 365 * n
yday := int(d)
mut day := yday
year := abs / int(3.154e+7) + 1970 //int(i64(y) + absoluteZeroYear)
hour := int(abs%secondsPerDay) / secondsPerHour
minute := int(abs % secondsPerHour) / secondsPerMinute
second := int(abs % secondsPerMinute)
if is_leap_year(year) {
// Leap year
if day > 31+29-1 {
// After leap day; pretend it wasn't there.
day--
} else if day == 31+29-1 {
// Leap day.
day = 29
return Time{year:year, month:2, day:day, hour:hour, minute: minute, second: second}
}
}
// Estimate month on assumption that every month has 31 days.
// The estimate may be too low by at most one month, so adjust.
mut month := day / 31
mut begin := 0
end := int(daysBefore[month+1])
if day >= end {
month++
begin = end
} else {
begin = int(daysBefore[month])
}
month++ // because January is 1
day = day - begin + 1
return Time{year:year, month: month, day:day, hour:hour, minute: minute, second: second}
}
pub fn convert_ctime(t tm) Time {

View File

@@ -38,3 +38,16 @@ fn test_days_in_month() {
assert check(11, 2001, 30) // November
assert check(12, 2001, 31) // December
}
fn test_unix() {
t := time.unix(1564366499)
assert t.year == 2019
assert t.month == 7
assert t.day == 29
assert t.hour == 2
assert t.minute == 14
//assert t.second == 32 // TODO broken
}

69
vlib/vweb/tmpl/tmpl.v Normal file
View File

@@ -0,0 +1,69 @@
module tmpl
import os
import strings
const (
STR_START = 'sb.write(\''
STR_END = '\' ) '
)
pub fn compile_template(path string) string {
//lines := os.read_lines(path)
mut html := os.read_file(path) or {
panic('html failed')
return ''
}
mut header := ''
if os.file_exists('header.html') {
h := os.read_file('header.html') or {
panic('html failed')
return ''
}
header = h
}
lines := html.split_into_lines()
mut s := strings.new_builder(1000)
base := path.all_after('/').replace('.html', '')
s.writeln('module main import strings fn ${base}_view() string { // this line will get removed becase only function body is embedded
mut sb := strings.new_builder(${lines.len * 30})
header := \'$header\'
_ := header
//footer := \'footer\'
')
s.writeln(STR_START)
for line in lines {
if line.contains('@if ') {
s.writeln(STR_END)
pos := line.index('@if')
s.writeln('if ' + line.right(pos + 4) + '{')
s.writeln(STR_START)
}
else if line.contains('@end') {
s.writeln(STR_END)
s.writeln('}')
s.writeln(STR_START)
}
else if line.contains('@else') {
s.writeln(STR_END)
s.writeln(' } else { ')
s.writeln(STR_START)
}
else if line.contains('@for') {
s.writeln(STR_END)
pos := line.index('@for')
s.writeln('for ' + line.right(pos + 4) + '{')
s.writeln(STR_START)
}
// @name
else {
s.writeln(line.replace('@', '\x24').replace('\'', '"') )
}
}
s.writeln(STR_END)
s.writeln('tmpl_res := sb.str() ')
s.writeln('return tmpl_res }')
return s.str()
}

121
vlib/vweb/vweb.v Normal file
View File

@@ -0,0 +1,121 @@
module vweb
import (
os
strings
net
http
)
struct Context {
pub:
req http.Request
conn net.Socket
post_form map[string]string
// TODO Response
headers []string
}
pub fn (ctx Context) write(s string) {
//ctx.conn.write(s)
}
pub fn (ctx Context) redirect(url string) {
h := ctx.headers.join('\n')
ctx.conn.write('
HTTP/1.1 302 Found
Location: $url
$h
')
}
pub fn (ctx mut Context) set_cookie(key, val string) {
ctx.set_header('Set-Cookie', '$key=$val')
}
pub fn (ctx Context) get_cookie(key string) string {
cookie := ctx.req.headers['Cookie']
return cookie.find_between('$key=', ';')
}
fn (ctx mut Context) set_header(key, val string) {
// ctx.resp.headers[key] = val
ctx.headers << '$key: $val'
}
pub fn (ctx Context) html(html string) {
//tmpl := os.read_file(path) or {return}
ctx.conn.write('HTTP/1.1 200 OK
Content-Type: text/html
$html
')
}
pub fn run<T>(port int) {
l := net.listen(port) or { panic('failed to listen') return }
for {
conn := l.accept() or {
panic('accept() failed')
return
}
// TODO move this to handle_conn<T>(conn, app)
s := conn.read_line()
// Parse the first line
// "GET / HTTP/1.1"
first_line := s.all_before('\n')
vals := first_line.split(' ')
mut action := vals[1].right(1).all_before('/')
if action == '' {
action = 'index'
}
req := http.Request{
headers: map[string]string{}
ws_func: 0
user_ptr: 0
method: vals[0]
url: vals[1]
}
mut app := T{
vweb: Context{
req: req
conn: conn
post_form: map[string]string{}
}
}
app.init()
if req.method == 'POST' {
app.vweb.parse_form(s)
}
println('vweb action = "$action"')
if vals.len < 2 {
println('no vals for http')
return
}
app.$action()
conn.close()
}
}
fn (ctx mut Context) parse_form(s string) {
if ctx.req.method != 'POST' {
return
}
pos := s.index('\r\n\r\n')
if pos > -1 {
mut str_form := s.substr(pos, s.len)
str_form = str_form.replace('+', ' ')
words := str_form.split('&')
for word in words {
keyval := word.split('=')
key := keyval[0]
val := keyval[1]
//println('http form $key => $val')
ctx.post_form[key] = http.unescape(val)
}
}
}