vweb: first step to revive it

pull/2535/head
Alexander Medvednikov 2019-10-24 19:44:49 +03:00
parent 2032da7fe2
commit 892d1c6aab
7 changed files with 116 additions and 114 deletions

View File

@ -3,7 +3,7 @@ module main
import vweb import vweb
const ( const (
Port = 8082 port = 8082
) )
struct App { struct App {
@ -13,7 +13,9 @@ pub mut:
} }
fn main() { fn main() {
vweb.run<App>(Port) mut app := App{}
vweb.run(app, port)
//vweb.run<App>(Port)
} }
pub fn (app mut App) init() { pub fn (app mut App) init() {

View File

@ -275,8 +275,8 @@ fn (p mut Parser) fn_decl() {
// C function header def? (fn C.NSMakeRect(int,int,int,int)) // C function header def? (fn C.NSMakeRect(int,int,int,int))
is_c := f.name == 'C' && p.tok == .dot is_c := f.name == 'C' && p.tok == .dot
// Just fn signature? only builtin.v + default build mode // Just fn signature? only builtin.v + default build mode
if p.pref.build_mode == .build_module { if p.pref.is_verbose { // p.pref.build_mode == .build_module {
//println('\n\nfn_decl() name=$f.name receiver_typ=$receiver_typ nogen=$p.cgen.nogen') println('\n\nfn_decl() name=$f.name receiver_typ=$receiver_typ nogen=$p.cgen.nogen')
} }
if is_c { if is_c {
p.check(.dot) p.check(.dot)
@ -561,8 +561,10 @@ fn (p mut Parser) check_unused_variables() {
if !var.is_used && !p.pref.is_repl && !var.is_arg && !p.pref.translated { if !var.is_used && !p.pref.is_repl && !var.is_arg && !p.pref.translated {
p.production_error_with_token_index('`$var.name` declared and not used', var.token_idx ) p.production_error_with_token_index('`$var.name` declared and not used', var.token_idx )
} }
if !var.is_changed && var.is_mut && !p.pref.is_repl && !p.pref.translated { if !var.is_changed && var.is_mut && !p.pref.is_repl &&
p.error_with_token_index( '`$var.name` is declared as mutable, but it was never changed', var.token_idx ) !p.pref.translated && var.typ != 'T*'
{
p.error_with_token_index('`$var.name` is declared as mutable, but it was never changed', var.token_idx )
} }
} }
} }

View File

@ -472,7 +472,7 @@ fn (p mut Parser) import_statement() {
if p.tok != .name { if p.tok != .name {
p.error('bad import format') p.error('bad import format')
} }
if p.peek() == .number { // && p.scanner.text[p.scanner.pos + 1] == `.` { if p.peek() == .number {
p.error('bad import format. module/submodule names cannot begin with a number') p.error('bad import format. module/submodule names cannot begin with a number')
} }
import_tok_idx := p.token_idx-1 import_tok_idx := p.token_idx-1
@ -513,14 +513,6 @@ fn (p mut Parser) const_decl() {
if is_pub { if is_pub {
p.next() p.next()
} }
if p.tok == .key_import {
p.error_with_token_index(
'`import const` was removed from the language, ' +
'because predeclaring C constants is not needed anymore. ' +
'You can use them directly with C.CONST_NAME',
p.cur_tok_index()
)
}
p.inside_const = true p.inside_const = true
p.check(.key_const) p.check(.key_const)
p.fspace() p.fspace()
@ -659,13 +651,13 @@ fn (p mut Parser) interface_method(field_name, receiver string) &Fn {
fn key_to_type_cat(tok TokenKind) TypeCategory { fn key_to_type_cat(tok TokenKind) TypeCategory {
match tok { match tok {
.key_interface { return TypeCategory.interface_ } .key_interface { return .interface_ }
.key_struct { return TypeCategory.struct_ } .key_struct { return .struct_ }
.key_union { return TypeCategory.union_ } .key_union { return .union_ }
//TokenKind.key_ => return .interface_ //TokenKind.key_ => return .interface_
} }
verror('Unknown token: $tok') verror('Unknown token: $tok')
return TypeCategory.builtin return .builtin
} }
// check_name checks for a name token and returns its literal // check_name checks for a name token and returns its literal
@ -727,10 +719,10 @@ fn (p mut Parser) check(expected TokenKind) {
*/ */
p.next() p.next()
if p.scanner.line_comment != '' { //if p.scanner.line_comment != '' {
//p.fgenln('// ! "$p.scanner.line_comment"') //p.fgenln('// ! "$p.scanner.line_comment"')
//p.scanner.line_comment = '' //p.scanner.line_comment = ''
} //}
} }
@ -894,7 +886,7 @@ fn (p mut Parser) get_type() string {
p.error('unknown type `$typ`') p.error('unknown type `$typ`')
} }
} }
else if !t.is_public && t.mod != p.mod && t.name != '' { else if !t.is_public && t.mod != p.mod && t.name != '' && !p.first_pass() {
p.error('type `$t.name` is private') p.error('type `$t.name` is private')
} }
} }

View File

@ -81,6 +81,7 @@ fn (p mut Parser) struct_decl() {
typ.is_placeholder = false typ.is_placeholder = false
typ.cat = cat typ.cat = cat
typ.parent = objc_parent typ.parent = objc_parent
typ.is_public = is_pub
p.table.rewrite_type(typ) p.table.rewrite_type(typ)
} }
else { else {

View File

@ -11,9 +11,9 @@ const (
max_redirects = 4 max_redirects = 4
) )
struct Request { pub struct Request {
pub: pub:
headers map[string]string headers map[string]string
method string method string
// cookies map[string]string // cookies map[string]string
h string h string
@ -27,10 +27,10 @@ pub:
user_agent string user_agent string
} }
struct Response { pub struct Response {
pub: pub:
text string text string
headers map[string]string headers map[string]string
status_code int status_code int
} }
@ -56,7 +56,7 @@ pub fn post(url, data string) ?Response {
pub fn new_request(typ, _url, _data string) ?Request { pub fn new_request(typ, _url, _data string) ?Request {
if _url == '' { if _url == '' {
return error('bad url') return error('bad url')
} }
mut url := _url mut url := _url
mut data := _data mut data := _data
@ -77,9 +77,9 @@ pub fn new_request(typ, _url, _data string) ?Request {
} }
pub fn get_text(url string) string { pub fn get_text(url string) string {
resp := get(url) or { return '' } resp := get(url) or { return '' }
return resp.text return resp.text
} }
fn (req mut Request) free() { fn (req mut Request) free() {
req.headers.free() req.headers.free()
@ -172,29 +172,29 @@ fn (req &Request) method_and_url_to_response(method string, url net_dot_urllib.U
fn parse_response(resp string) Response { fn parse_response(resp string) Response {
mut headers := map[string]string mut headers := map[string]string
first_header := resp.all_before('\n') first_header := resp.all_before('\n')
mut status_code := 0 mut status_code := 0
if first_header.contains('HTTP/') { if first_header.contains('HTTP/') {
val := first_header.find_between(' ', ' ') val := first_header.find_between(' ', ' ')
status_code = val.int() status_code = val.int()
} }
mut text := '' mut text := ''
// Build resp headers map and separate the body // Build resp headers map and separate the body
mut nl_pos := 3 mut nl_pos := 3
mut i := 1 mut i := 1
for { for {
old_pos := nl_pos old_pos := nl_pos
nl_pos = resp.index_after('\n', nl_pos+1) nl_pos = resp.index_after('\n', nl_pos+1)
if nl_pos == -1 { if nl_pos == -1 {
break break
} }
h := resp.substr(old_pos + 1, nl_pos) h := resp.substr(old_pos + 1, nl_pos)
// End of headers // End of headers
if h.len <= 1 { if h.len <= 1 {
text = resp.right(nl_pos + 1) text = resp.right(nl_pos + 1)
break break
} }
i++ i++
pos := h.index(':') pos := h.index(':')
if pos == -1 { if pos == -1 {
continue continue
@ -212,9 +212,9 @@ fn parse_response(resp string) Response {
} }
return Response { return Response {
status_code: status_code status_code: status_code
headers: headers headers: headers
text: text text: text
} }
} }
@ -222,33 +222,33 @@ fn (req &Request) build_request_headers(method, host_name, path string) string {
ua := req.user_agent ua := req.user_agent
mut uheaders := []string mut uheaders := []string
for key, val in req.headers { for key, val in req.headers {
uheaders << '${key}: ${val}\r\n' uheaders << '${key}: ${val}\r\n'
} }
if req.data.len > 0 { if req.data.len > 0 {
uheaders << 'Content-Length: ${req.data.len}\r\n' uheaders << 'Content-Length: ${req.data.len}\r\n'
} }
return '$method $path HTTP/1.1\r\n' + return '$method $path HTTP/1.1\r\n' +
'Host: $host_name\r\n' + 'Host: $host_name\r\n' +
'User-Agent: $ua\r\n' + 'User-Agent: $ua\r\n' +
uheaders.join('') + uheaders.join('') +
'Connection: close\r\n\r\n' + 'Connection: close\r\n\r\n' +
req.data req.data
} }
pub fn unescape_url(s string) string { pub fn unescape_url(s string) string {
panic('http.unescape_url() was replaced with urllib.query_unescape()') panic('http.unescape_url() was replaced with urllib.query_unescape()')
} }
pub fn escape_url(s string) string { pub fn escape_url(s string) string {
panic('http.escape_url() was replaced with urllib.query_escape()') panic('http.escape_url() was replaced with urllib.query_escape()')
} }
pub fn unescape(s string) string { pub fn unescape(s string) string {
panic('http.unescape() was replaced with http.unescape_url()') panic('http.unescape() was replaced with http.unescape_url()')
} }
pub fn escape(s string) string { pub fn escape(s string) string {
panic('http.escape() was replaced with http.escape_url()') panic('http.escape() was replaced with http.escape_url()')
} }
type wsfn fn (s string, ptr voidptr) type wsfn fn (s string, ptr voidptr)

View File

@ -2,7 +2,7 @@ module net
import os import os
struct Socket { pub struct Socket {
pub: pub:
sockfd int sockfd int
family int family int

View File

@ -1,11 +1,15 @@
// Copyright (c) 2019 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 vweb module vweb
import ( import (
os os
net net
http http
net.urllib net.urllib
) )
const ( const (
methods_with_form = ['POST', 'PUT', 'PATCH'] methods_with_form = ['POST', 'PUT', 'PATCH']
@ -27,16 +31,16 @@ const (
} }
) )
struct Context { pub struct Context {
static_files map[string]string static_files map[string]string
static_mime_types map[string]string static_mime_types map[string]string
pub: pub:
req http.Request req http.Request
conn net.Socket conn net.Socket
form map[string]string form map[string]string
// TODO Response // TODO Response
mut: mut:
headers string // response headers headers string // response headers
} }
pub fn (ctx Context) html(html string) { pub fn (ctx Context) html(html string) {
@ -48,11 +52,11 @@ pub fn (ctx Context) text(s string) {
} }
pub fn (ctx Context) json(s string) { pub fn (ctx Context) json(s string) {
ctx.conn.write('HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n$ctx.headers\r\n\r\n$s') ctx.conn.write('HTTP/1.1 200 OK\r\nContent-Type: application/json\r\n$ctx.headers\r\n\r\n$s')
} }
pub fn (ctx Context) redirect(url string) { pub fn (ctx Context) redirect(url string) {
ctx.conn.write('HTTP/1.1 302 Found\r\nLocation: $url\r\n\r\n$ctx.headers') ctx.conn.write('HTTP/1.1 302 Found\r\nLocation: $url\r\n\r\n$ctx.headers')
} }
pub fn (ctx Context) not_found(s string) { pub fn (ctx Context) not_found(s string) {
@ -84,15 +88,16 @@ fn (ctx mut Context) get_header(key string) string {
return ctx.headers.find_between('\r\n$key: ', '\r\n') return ctx.headers.find_between('\r\n$key: ', '\r\n')
} }
pub fn run<T>(port int) { //pub fn run<T>(port int) {
println('Running vweb app on http://localhost:$port ...') pub fn run<T>(app T, port int) {
l := net.listen(port) or { panic('failed to listen') } println('Running vweb app on http://localhost:$port ...')
mut app := T{} l := net.listen(port) or { panic('failed to listen') }
app.init() //mut app := T{}
app.init()
for { for {
conn := l.accept() or { conn := l.accept() or {
panic('accept() failed') panic('accept() failed')
} }
//foobar<T>() //foobar<T>()
// TODO move this to handle_conn<T>(conn, app) // TODO move this to handle_conn<T>(conn, app)
s := conn.read_line() s := conn.read_line()
@ -123,35 +128,35 @@ pub fn run<T>(port int) {
ws_func: 0 ws_func: 0
user_ptr: 0 user_ptr: 0
method: vals[0] method: vals[0]
url: vals[1] url: vals[1]
} }
$if debug { $if debug {
println('vweb action = "$action"') println('vweb action = "$action"')
} }
//mut app := T{ //mut app := T{
app.vweb = Context{ app.vweb = Context{
req: req req: req
conn: conn conn: conn
form: map[string]string form: map[string]string
static_files: map[string]string static_files: map[string]string
static_mime_types: map[string]string static_mime_types: map[string]string
} }
//} //}
if req.method in methods_with_form { if req.method in methods_with_form {
app.vweb.parse_form(s) app.vweb.parse_form(s)
} }
if vals.len < 2 { if vals.len < 2 {
$if debug { $if debug {
println('no vals for http') println('no vals for http')
} }
conn.close() conn.close()
continue continue
} }
// Serve a static file if it's one // Serve a static file if it's one
// if app.vweb.handle_static() { // if app.vweb.handle_static() {
// conn.close() // conn.close()
// continue // continue
// } // }
// Call the right action // Call the right action
@ -163,13 +168,13 @@ pub fn run<T>(port int) {
} }
pub fn foobar<T>() { pub fn foobar<T>() {
} }
fn (ctx mut Context) parse_form(s string) { fn (ctx mut Context) parse_form(s string) {
if !(ctx.req.method in methods_with_form) { if !(ctx.req.method in methods_with_form) {
return return
} }
pos := s.index('\r\n\r\n') pos := s.index('\r\n\r\n')
if pos > -1 { if pos > -1 {
mut str_form := s.substr(pos, s.len) mut str_form := s.substr(pos, s.len)
@ -177,18 +182,18 @@ fn (ctx mut Context) parse_form(s string) {
words := str_form.split('&') words := str_form.split('&')
for word in words { for word in words {
$if debug { $if debug {
println('parse form keyval="$word"') println('parse form keyval="$word"')
} }
keyval := word.trim_space().split('=') keyval := word.trim_space().split('=')
if keyval.len != 2 { continue } if keyval.len != 2 { continue }
key := keyval[0] key := keyval[0]
val := urllib.query_unescape(keyval[1]) or { val := urllib.query_unescape(keyval[1]) or {
continue continue
} }
$if debug { $if debug {
println('http form "$key" => "$val"') println('http form "$key" => "$val"')
} }
ctx.form[key] = val ctx.form[key] = val
} }
} }
} }
@ -227,15 +232,15 @@ pub fn (ctx mut Context) handle_static(directory_path string) bool {
static_file := ctx.static_files[ctx.req.url] static_file := ctx.static_files[ctx.req.url]
mime_type := ctx.static_mime_types[ctx.req.url] mime_type := ctx.static_mime_types[ctx.req.url]
if static_file != '' { if static_file != '' {
data := os.read_file(static_file) or { return false } data := os.read_file(static_file) or { return false }
ctx.conn.write('HTTP/1.1 200 OK\r\nContent-Type: $mime_type\r\n\r\n$data') ctx.conn.write('HTTP/1.1 200 OK\r\nContent-Type: $mime_type\r\n\r\n$data')
return true return true
} }
return false return false
} }
pub fn (ctx mut Context) serve_static(url, file_path, mime_type string) { pub fn (ctx mut Context) serve_static(url, file_path, mime_type string) {
ctx.static_files[url] = file_path ctx.static_files[url] = file_path
ctx.static_mime_types[url] = mime_type ctx.static_mime_types[url] = mime_type
} }