Initial project import
This commit is contained in:
commit
23f2d29e32
11957 files changed
+1754577
No files matched your search
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,105 @@
|
||||
// Copyright (c) 2026 Alexander Medvednikov. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
// vtest build: !windows
|
||||
module parser
|
||||
|
||||
import os
|
||||
import v2.pref
|
||||
import v2.token
|
||||
|
||||
fn parse_code_for_test(code string) {
|
||||
tmp_file := '/tmp/v2_parser_test_${os.getpid()}.v'
|
||||
os.write_file(tmp_file, code) or { panic('failed to write temp file') }
|
||||
defer {
|
||||
os.rm(tmp_file) or {}
|
||||
}
|
||||
prefs := &pref.Preferences{}
|
||||
mut file_set := token.FileSet.new()
|
||||
mut par := Parser.new(prefs)
|
||||
files := par.parse_files([tmp_file], mut file_set)
|
||||
assert files.len == 1
|
||||
}
|
||||
|
||||
fn test_if_condition_parses_selector_struct_init_before_infix() {
|
||||
parse_code_for_test('
|
||||
module main
|
||||
|
||||
fn main() {
|
||||
mut pos := token.Pos{}
|
||||
if pos == token.Pos{} && true {
|
||||
x := 1
|
||||
}
|
||||
}
|
||||
')
|
||||
}
|
||||
|
||||
fn test_if_condition_keeps_uppercase_selector_followed_by_block() {
|
||||
parse_code_for_test('
|
||||
module main
|
||||
|
||||
fn main() int {
|
||||
ch := 0
|
||||
if ch == C.EOF {
|
||||
return -1
|
||||
}
|
||||
return ch
|
||||
}
|
||||
')
|
||||
}
|
||||
|
||||
fn test_match_branch_keeps_empty_uppercase_branch_block() {
|
||||
parse_code_for_test('
|
||||
module main
|
||||
|
||||
type Obj = EmptyScopeObject | AsmRegister
|
||||
|
||||
struct AsmRegister {}
|
||||
struct EmptyScopeObject {}
|
||||
|
||||
fn main() {
|
||||
node := Obj(EmptyScopeObject{})
|
||||
match node {
|
||||
AsmRegister, EmptyScopeObject {}
|
||||
}
|
||||
}
|
||||
')
|
||||
}
|
||||
|
||||
fn test_global_unsafe_block_parses_leading_array_literal_stmt() {
|
||||
parse_code_for_test('
|
||||
module main
|
||||
|
||||
struct File {}
|
||||
|
||||
__global files = unsafe { []&File{} }
|
||||
|
||||
fn main() {}
|
||||
')
|
||||
}
|
||||
|
||||
fn test_array_literal_parses_backslash_char_literal() {
|
||||
parse_code_for_test('
|
||||
module main
|
||||
|
||||
fn main() {
|
||||
x := [u8(`\\\\`)]
|
||||
_ = x
|
||||
}
|
||||
')
|
||||
}
|
||||
|
||||
fn test_call_argument_parses_untyped_backslash_char_array_literal() {
|
||||
parse_code_for_test('
|
||||
module main
|
||||
|
||||
fn b(bytes []u8) []u8 {
|
||||
return bytes
|
||||
}
|
||||
|
||||
fn main() {
|
||||
x := b([`\\\\`])
|
||||
_ = x
|
||||
}
|
||||
')
|
||||
}
|
||||
@@ -0,0 +1,264 @@
|
||||
// Copyright (c) 2020-2024 Joe Conigliaro. All rights reserved.
|
||||
// Use of this source code is governed by an MIT license
|
||||
// that can be found in the LICENSE file.
|
||||
module parser
|
||||
|
||||
import v2.ast
|
||||
|
||||
@[inline]
|
||||
fn (mut p Parser) expect_type() ast.Expr {
|
||||
// return p.try_type() or {
|
||||
// p.error(err.msg())
|
||||
// }
|
||||
typ := p.try_type()
|
||||
if typ is ast.EmptyExpr {
|
||||
p.error('expecting type, got `${p.tok}`')
|
||||
}
|
||||
return typ
|
||||
}
|
||||
|
||||
// TODO: use result or stick with empty expr?
|
||||
// pub fn (mut p Parser) try_type() !ast.Expr {
|
||||
fn (mut p Parser) try_type() ast.Expr {
|
||||
match p.tok {
|
||||
// pointer: `&type`
|
||||
.amp {
|
||||
p.next()
|
||||
mut lifetime := ''
|
||||
if p.tok == .xor {
|
||||
p.next()
|
||||
lifetime = p.expect_name()
|
||||
}
|
||||
return ast.Type(ast.PointerType{
|
||||
base_type: p.expect_type()
|
||||
lifetime: lifetime
|
||||
})
|
||||
}
|
||||
// lifetime: `^a`
|
||||
.xor {
|
||||
pos := p.pos
|
||||
p.next()
|
||||
return ast.LifetimeExpr{
|
||||
name: p.expect_name()
|
||||
pos: pos
|
||||
}
|
||||
}
|
||||
// comptime type: `$enum` | `$struct` | etc
|
||||
.dollar {
|
||||
p.next()
|
||||
mut comptime_inner := ast.Expr(ast.empty_expr)
|
||||
if p.tok == .name {
|
||||
comptime_inner = ast.Expr(p.ident())
|
||||
} else {
|
||||
// TODO: match only allowed tokens otherwise error
|
||||
comptime_inner = ast.Expr(ast.Ident{
|
||||
pos: p.pos
|
||||
name: p.tok().str()
|
||||
})
|
||||
}
|
||||
return ast.ComptimeExpr{
|
||||
expr: comptime_inner
|
||||
}
|
||||
}
|
||||
// variadic: `...type`
|
||||
.ellipsis {
|
||||
p.next()
|
||||
// TODO: what will we use here?
|
||||
return ast.PrefixExpr{
|
||||
op: .ellipsis
|
||||
expr: p.expect_type()
|
||||
}
|
||||
}
|
||||
// atomic | shared
|
||||
// eg. typespec in struct field with modifier. other cases handled in expr()
|
||||
.key_atomic, .key_shared {
|
||||
kind := p.tok
|
||||
pos := p.pos
|
||||
p.next()
|
||||
return modifier_expr_with_expr(kind, p.expect_type(), pos)
|
||||
}
|
||||
// function: `fn(type) type`
|
||||
.key_fn {
|
||||
p.next()
|
||||
return ast.Type(p.fn_type())
|
||||
}
|
||||
// nil
|
||||
.key_nil {
|
||||
p.next()
|
||||
return ast.Type(ast.NilType{})
|
||||
}
|
||||
// none
|
||||
.key_none {
|
||||
p.next()
|
||||
return ast.Type(ast.NoneType{})
|
||||
}
|
||||
// inline / anonymous struct
|
||||
.key_struct {
|
||||
p.next()
|
||||
generic_params := if p.tok == .lsbr { p.generic_list() } else { []ast.Expr{} }
|
||||
embedded, fields := p.struct_decl_fields(.v, false, false)
|
||||
// TODO: should we use this or just StructDecl
|
||||
// even though it technically is not one? hrmm
|
||||
return ast.Type(ast.AnonStructType{
|
||||
generic_params: generic_params
|
||||
embedded: embedded
|
||||
fields: fields
|
||||
})
|
||||
}
|
||||
// tuple (multi return): `(type, type)`
|
||||
.lpar {
|
||||
p.next()
|
||||
// expect at least two (so we otherwise error)
|
||||
mut types := [p.expect_type()]
|
||||
p.expect(.comma)
|
||||
types << p.expect_type()
|
||||
// more than two
|
||||
for p.tok == .comma {
|
||||
p.next()
|
||||
types << p.expect_type()
|
||||
}
|
||||
p.expect(.rpar)
|
||||
return ast.Type(ast.TupleType{
|
||||
types: types
|
||||
})
|
||||
}
|
||||
// array: `[]type` | `[len]type`
|
||||
.lsbr {
|
||||
p.next()
|
||||
// dynamic array
|
||||
if p.tok == .rsbr {
|
||||
p.next()
|
||||
return ast.Type(ast.ArrayType{
|
||||
elem_type: p.expect_type()
|
||||
})
|
||||
}
|
||||
// fixed array
|
||||
len_expr := p.expr(.lowest)
|
||||
p.expect(.rsbr)
|
||||
return ast.Type(ast.ArrayFixedType{
|
||||
len: len_expr
|
||||
elem_type: p.expect_type()
|
||||
})
|
||||
}
|
||||
// name | chan | map
|
||||
.name {
|
||||
pos := p.pos
|
||||
// TODO: cleam this up
|
||||
name := p.ident_or_named_type()
|
||||
if p.tok == .lsbr && name !is ast.Type && p.tok != .semicolon {
|
||||
// TODO: is there a better solution than this. maybe it should be the
|
||||
// concern of p.fn_parameters() & p.struct_decl() rather than this?
|
||||
// `fn(param_a []type)` | `struct { field_a []type }`
|
||||
if name is ast.Ident && name.name.len + pos.offset < p.pos.offset {
|
||||
return name
|
||||
}
|
||||
// TODO: using ast.GenericArgs here may not be correct,
|
||||
// perhaps we should rename it to ast.GenericTypes
|
||||
// if name is ast.Ident && p.pos == pos + name.name.len { return name }
|
||||
return ast.Type(ast.GenericType{
|
||||
name: name
|
||||
params: p.generic_list()
|
||||
})
|
||||
}
|
||||
return name
|
||||
}
|
||||
// result: `!` | `!type`
|
||||
.not {
|
||||
p.next()
|
||||
return ast.Type(ast.ResultType{
|
||||
base_type: if p.tok != .semicolon { p.try_type() } else { ast.empty_expr }
|
||||
// base_type: p.tok != .semicolon { p.try_type() or { ast.empty_expr } } else { ast.empty_expr }
|
||||
})
|
||||
}
|
||||
// option: `?` | `?type`
|
||||
.question {
|
||||
p.next()
|
||||
return ast.Type(ast.OptionType{
|
||||
base_type: if p.tok != .semicolon { p.try_type() } else { ast.empty_expr }
|
||||
// base_type: if p.tok != .semicolon { p.try_type() or { ast.empty_expr } } else { ast.empty_expr }
|
||||
})
|
||||
}
|
||||
else {
|
||||
// return error('expecting type, got `${p.tok}`')
|
||||
return ast.empty_expr
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// function type / signature
|
||||
fn (mut p Parser) fn_type() ast.FnType {
|
||||
generic_params := if p.tok == .lsbr { p.generic_list() } else { []ast.Expr{} }
|
||||
params := p.fn_parameters()
|
||||
return_type := if p.tok != .semicolon { p.try_type() } else { ast.empty_expr }
|
||||
return fn_type_with_return_type(generic_params, params, return_type)
|
||||
}
|
||||
|
||||
fn fn_type_with_return_type(generic_params []ast.Expr, params []ast.Parameter, return_type ast.Expr) ast.FnType {
|
||||
mut fn_type := ast.FnType{
|
||||
generic_params: generic_params
|
||||
params: params
|
||||
return_type: ast.empty_expr
|
||||
}
|
||||
fn_type.return_type = return_type
|
||||
return fn_type
|
||||
}
|
||||
|
||||
// `ident` | `map[type]type | `(`chan`|`chan type`) | (`thread`|`thread type`)
|
||||
fn (mut p Parser) ident_or_named_type() ast.Expr {
|
||||
pos := p.pos
|
||||
lit := p.lit
|
||||
// `map[type]type`
|
||||
if lit == 'map' {
|
||||
p.next()
|
||||
if p.tok == .lsbr {
|
||||
p.next()
|
||||
key_type := p.expect_type()
|
||||
p.expect(.rsbr)
|
||||
return ast.Type(ast.MapType{
|
||||
key_type: key_type
|
||||
value_type: p.expect_type()
|
||||
})
|
||||
}
|
||||
// struct called `map` in builtin
|
||||
return ast.Ident{
|
||||
name: 'map'
|
||||
pos: pos
|
||||
}
|
||||
}
|
||||
// `chan` | `chan type`
|
||||
if lit == 'chan' {
|
||||
p.next()
|
||||
elem_type := if p.tok != .semicolon { p.try_type() } else { ast.empty_expr }
|
||||
if elem_type !is ast.EmptyExpr {
|
||||
return ast.Type(ast.ChannelType{
|
||||
elem_type: elem_type
|
||||
})
|
||||
}
|
||||
// struct called `chan` in builtin
|
||||
return ast.Ident{
|
||||
name: 'chan'
|
||||
pos: pos
|
||||
}
|
||||
}
|
||||
// `thread` | `thread type`
|
||||
else if lit == 'thread' {
|
||||
p.next()
|
||||
return ast.Type(ast.ThreadType{
|
||||
elem_type: if p.tok != .semicolon { p.try_type() } else { ast.empty_expr }
|
||||
})
|
||||
}
|
||||
// `ident` | `selector` - ident or type name
|
||||
return p.ident_or_selector_expr()
|
||||
}
|
||||
|
||||
// `ident` | (`Type`|`Type[T]`) | (`mod.Type`|`mod.Type[T]`)
|
||||
fn (mut p Parser) ident_or_type() ast.Expr {
|
||||
ident_or_selector := p.ident_or_selector_expr()
|
||||
if p.tok == .lsbr {
|
||||
return ast.Type(ast.GenericType{
|
||||
name: ident_or_selector
|
||||
params: p.generic_list()
|
||||
})
|
||||
}
|
||||
return ident_or_selector
|
||||
}
|
||||
Reference in new issue
Block a user