├── .gitignore
├── fmt_tool
├── tests
├── consts.v
├── comments.v
├── comments.v.fmted
├── consts.v.fmted
├── functions.v.fmted
├── if.v
├── functions.v
├── if.v.fmted
├── structs.v
├── structs.v.fmted
├── structs.v.c
├── comments.v.c
├── consts.v.c
├── functions.v.c
├── if.v.c
├── comments.v.pfile
├── consts.v.pfile
├── if.v.pfile
├── functions.v.pfile
└── structs.v.pfile
├── token
├── position.v
└── token.v
├── ast
├── file.v
├── attribute.v
├── expr.v
└── ast.v
├── error
└── error.v
├── parser
├── if.v
├── vars.v
├── fn.v
├── attrs.v
├── struct.v
└── parser.v
├── cmd
└── fmt.v
├── types
├── type.v
└── table.v
├── util
└── write_error_message.v
├── README.md
├── littleV.v
├── .github
└── workflows
│ └── ci.yml
├── gen
└── gen.v
├── fmt
└── fmt.v
├── scanner
└── scanner.v
└── LICENSE
/.gitignore:
--------------------------------------------------------------------------------
1 | littleV
--------------------------------------------------------------------------------
/fmt_tool:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/LouisSchmieder/lilV/HEAD/fmt_tool
--------------------------------------------------------------------------------
/tests/consts.v:
--------------------------------------------------------------------------------
1 | module main
2 |
3 | pub const abc = 'def'
4 |
5 | const (
6 | test = 123
7 | abc = 544
8 | )
--------------------------------------------------------------------------------
/tests/comments.v:
--------------------------------------------------------------------------------
1 | module main
2 |
3 | import test as abc
4 |
5 | // test
6 | /* test */
7 |
8 | pub fn main() {}
9 |
--------------------------------------------------------------------------------
/tests/comments.v.fmted:
--------------------------------------------------------------------------------
1 | module main
2 |
3 | import test as abc
4 |
5 | // test
6 | // test
7 | pub fn main () {}
8 |
--------------------------------------------------------------------------------
/tests/consts.v.fmted:
--------------------------------------------------------------------------------
1 | module main
2 |
3 | pub const abc = 'def'
4 |
5 | const (
6 | test = 123
7 | abc = 544
8 | )
9 |
--------------------------------------------------------------------------------
/token/position.v:
--------------------------------------------------------------------------------
1 | module token
2 |
3 | struct Position {
4 | pub:
5 | file string
6 | line_nr int
7 | char_nr int
8 | }
--------------------------------------------------------------------------------
/tests/functions.v.fmted:
--------------------------------------------------------------------------------
1 | module test
2 |
3 | fn test (test i8) int {
4 | }
5 |
6 | ['test': 123; abc]
7 | fn main () {
8 | test (abc)
9 | }
10 |
--------------------------------------------------------------------------------
/ast/file.v:
--------------------------------------------------------------------------------
1 | module ast
2 |
3 | import types
4 |
5 | pub struct File {
6 | pub:
7 | mod string
8 | stmts []Stmt
9 | pub mut:
10 | table &types.Table
11 | }
--------------------------------------------------------------------------------
/tests/if.v:
--------------------------------------------------------------------------------
1 | module main
2 |
3 | fn main() {
4 | if ab {
5 | eprintln('test')
6 | } else if c {
7 | eprintln('abc')
8 | } else {
9 | eprintln(123)
10 | }
11 | }
--------------------------------------------------------------------------------
/tests/functions.v:
--------------------------------------------------------------------------------
1 | module test
2 |
3 | fn test (test i8) int {
4 | return int(test)
5 | }
6 |
7 | ['test': 123; abc]
8 | fn main () {
9 | abc := 123
10 | test (abc)
11 | }
12 |
--------------------------------------------------------------------------------
/tests/if.v.fmted:
--------------------------------------------------------------------------------
1 | module main
2 |
3 | fn main () {
4 | if ab {
5 | eprintln.eprintln ('test')
6 | } else if c {
7 | eprintln.eprintln ('abc')
8 | } else {
9 | eprintln.eprintln (123)
10 | }
11 | }
12 |
--------------------------------------------------------------------------------
/error/error.v:
--------------------------------------------------------------------------------
1 | module error
2 |
3 | import token
4 |
5 | pub enum Level {
6 | warn
7 | error
8 | }
9 |
10 | pub struct Error {
11 | pub:
12 | pos token.Position
13 | len int
14 | level Level
15 | msg string
16 | }
--------------------------------------------------------------------------------
/ast/attribute.v:
--------------------------------------------------------------------------------
1 | module ast
2 |
3 | pub struct Attribute {
4 | pub:
5 | name string
6 | name_kind AttribKind
7 | has_arg bool
8 | arg string
9 | arg_kind AttribKind
10 | }
11 |
12 | pub enum AttribKind {
13 | string
14 | name
15 | number
16 | }
--------------------------------------------------------------------------------
/tests/structs.v:
--------------------------------------------------------------------------------
1 | module main
2 |
3 | pub type ABC = byte | int | Name | Test | abc | string | u64 | test2
4 |
5 | [abc]
6 | struct Name {
7 | test u32
8 | mut:
9 | field byte [help]
10 | nums []int
11 | pub:
12 | abc int
13 | pub mut:
14 | cd u64
15 | }
--------------------------------------------------------------------------------
/tests/structs.v.fmted:
--------------------------------------------------------------------------------
1 | module main
2 |
3 | pub type ABC = byte | int | Name | Test | abc | string | u64 |
4 | test2
5 |
6 | [abc]
7 | struct Name {
8 | test u32
9 | mut:
10 | field byte [help]
11 | nums []int
12 | pub:
13 | abc int
14 | pub mut:
15 | cd u64
16 |
--------------------------------------------------------------------------------
/tests/structs.v.c:
--------------------------------------------------------------------------------
1 | typedef signed char i8;
2 | typedef signed short i16;
3 | typedef signed long int;
4 | typedef signed long long i64;
5 | typedef unsigned char byte;
6 | typedef unsigned short u16;
7 | typedef unsigned long u32;
8 | typedef unsigned long long u64;
9 | typedef char * charptr;
10 | typedef void * voidptr;
11 | typedef void void_;
12 |
--------------------------------------------------------------------------------
/tests/comments.v.c:
--------------------------------------------------------------------------------
1 | typedef signed char i8;
2 | typedef signed short i16;
3 | typedef signed long int;
4 | typedef signed long long i64;
5 | typedef unsigned char byte;
6 | typedef unsigned short u16;
7 | typedef unsigned long u32;
8 | typedef unsigned long long u64;
9 | typedef char * charptr;
10 | typedef void * voidptr;
11 | typedef void void_;
12 | void_ main__main();
13 | void_ main__main() {
14 | }
15 |
--------------------------------------------------------------------------------
/tests/consts.v.c:
--------------------------------------------------------------------------------
1 | typedef signed char i8;
2 | typedef signed short i16;
3 | typedef signed long int;
4 | typedef signed long long i64;
5 | typedef unsigned char byte;
6 | typedef unsigned short u16;
7 | typedef unsigned long u32;
8 | typedef unsigned long long u64;
9 | typedef char * charptr;
10 | typedef void * voidptr;
11 | typedef void void_;
12 | #define main__abc "def"
13 | #define main__test 123
14 | #define main__abc 544
15 |
--------------------------------------------------------------------------------
/ast/expr.v:
--------------------------------------------------------------------------------
1 | module ast
2 |
3 | import types
4 |
5 | pub fn (mut file File) get_type(expr Expr) types.Type {
6 | match expr {
7 | StringExpr {
8 | return file.table.find_type('string') or {types.Type{}}
9 | }
10 | NumberExpr {
11 | return file.table.find_type('int') or {types.Type{}}
12 | }
13 | CastExpr {
14 | return expr.typ
15 | }
16 | IdentExpr {
17 | return file.get_type(expr.expr)
18 | }
19 | else {
20 | return types.Type{}
21 | }
22 | }
23 | }
--------------------------------------------------------------------------------
/tests/functions.v.c:
--------------------------------------------------------------------------------
1 | typedef signed char i8;
2 | typedef signed short i16;
3 | typedef signed long int;
4 | typedef signed long long i64;
5 | typedef unsigned char byte;
6 | typedef unsigned short u16;
7 | typedef unsigned long u32;
8 | typedef unsigned long long u64;
9 | typedef char * charptr;
10 | typedef void * voidptr;
11 | typedef void void_;
12 | int test__test(i8 test);
13 | void_ test__main();
14 | int test__test(i8 test) {
15 | return ((int)test);
16 | }
17 | void_ test__main() {
18 | int abc = 123;
19 | test__test(abc);
20 | }
21 |
--------------------------------------------------------------------------------
/tests/if.v.c:
--------------------------------------------------------------------------------
1 | typedef signed char i8;
2 | typedef signed short i16;
3 | typedef signed long int;
4 | typedef signed long long i64;
5 | typedef unsigned char byte;
6 | typedef unsigned short u16;
7 | typedef unsigned long u32;
8 | typedef unsigned long long u64;
9 | typedef char * charptr;
10 | typedef void * voidptr;
11 | typedef void void_;
12 | void_ main__main();
13 | void_ main__main() {
14 | if (ab){
15 | eprintln__eprintln("test");
16 | } else if(c) {
17 | eprintln__eprintln("abc");
18 | } else {
19 | eprintln__eprintln(123);
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/parser/if.v:
--------------------------------------------------------------------------------
1 | module parser
2 |
3 | import ast
4 |
5 | fn (mut p Parser) parse_if() ast.IfStmt {
6 | p.expect(.key_if)
7 | pos := p.tok.pos
8 | p.next()
9 | expr := p.expr()
10 | stmts := p.parse_block()
11 | mut elses := []ast.ElseStmt{}
12 | for {
13 | p.next()
14 | epos := p.tok.pos
15 | if p.tok.kind != .key_else {
16 | break
17 | }
18 | p.next()
19 | has_cond := p.tok.kind == .key_if
20 | mut e := ast.Expr(ast.Unknown{})
21 | if has_cond {
22 | p.next()
23 | e = p.expr()
24 | }
25 | block := p.parse_block()
26 | elses << ast.ElseStmt{
27 | pos: epos
28 | has_cond: has_cond
29 | cond: e
30 | stmts: block
31 | }
32 | }
33 | return ast.IfStmt{
34 | pos: pos
35 | cond: expr
36 | stmts: stmts
37 | elses: elses
38 | }
39 | }
--------------------------------------------------------------------------------
/parser/vars.v:
--------------------------------------------------------------------------------
1 | module parser
2 |
3 | import ast
4 | import token
5 |
6 | fn (mut p Parser) consts(is_pub bool) ast.ConstStmt {
7 | pos := p.pos()
8 | p.next()
9 | if p.tok.kind != .lpar {
10 | c := [p.parse_const_line()]
11 | return ast.ConstStmt {
12 | pos: pos
13 | is_pub: is_pub
14 | consts: c
15 | }
16 | }
17 | p.next()
18 | mut consts := []ast.Const{}
19 | for p.tok.kind == .name {
20 | consts << p.parse_const_line()
21 | }
22 | p.expect(.rpar)
23 | p.next()
24 | return ast.ConstStmt{
25 | pos: pos
26 | is_pub: is_pub
27 | consts: consts
28 | }
29 | }
30 |
31 | fn (mut p Parser) parse_const_line() ast.Const {
32 | pos := p.pos()
33 | name := p.get_name()
34 | p.next()
35 | p.expect(.assign)
36 | p.next()
37 | expr := p.expr()
38 | return ast.Const{
39 | pos: pos
40 | name: name
41 | expr: expr
42 | }
43 | }
44 |
45 | fn (mut p Parser) decl_stmt(pos token.Position, name string) ast.DeclareStmt {
46 | p.next()
47 | expr := p.expr()
48 | return ast.DeclareStmt{
49 | pos: pos
50 | name: name
51 | expr: expr
52 | }
53 | }
--------------------------------------------------------------------------------
/cmd/fmt.v:
--------------------------------------------------------------------------------
1 | module main
2 |
3 | import os
4 | import scanner
5 | import parser
6 | import fmt
7 | import util
8 |
9 | fn main() {
10 | args := os.args[1..]
11 |
12 | verify := args[0] == '-verify'
13 | write := args[0] == '-w'
14 |
15 | file := args.last()
16 |
17 | data := os.read_file(file) or { '' }
18 | mut scan := scanner.create_scanner(data, file)
19 | mut pars := parser.create_parser(scan)
20 | out, err := pars.parse_file()
21 | if err.len > 0 {
22 | warns := err.filter(it.level == .warn)
23 | for warn in warns {
24 | util.write_error_message(warn, data)
25 | }
26 | errors := err.filter(it.level == .error)
27 | for error in errors {
28 | util.write_error_message(error, data)
29 | }
30 | if errors.len > 0 {
31 | exit(1)
32 | }
33 | }
34 |
35 |
36 | mut f := fmt.create_fmt(out)
37 | res := f.format()
38 |
39 | if verify {
40 | is_formatted := res == data
41 | eprintln('`$file` formatted: $is_formatted')
42 | } else if write {
43 | os.write_file(file, res) or {
44 | panic(err)
45 | }
46 | eprintln('Formatted $file')
47 | } else {
48 | eprintln(res)
49 | }
50 | }
--------------------------------------------------------------------------------
/types/type.v:
--------------------------------------------------------------------------------
1 | module types
2 |
3 | pub enum Kind {
4 | struct_
5 | sumtype
6 | builtin
7 | }
8 |
9 | pub type Info = Struct | SumType | Builtin
10 |
11 | pub struct Type {
12 | pub:
13 | mod string
14 | name string
15 | tname string
16 | bname string // backend name
17 | kind Kind
18 | info Info
19 | mut:
20 | is_array bool
21 | }
22 |
23 | pub fn (mut t Type) set_array() {
24 | t.is_array = true
25 | }
26 |
27 | pub fn (t Type) bname() string {
28 | arr := if t.is_array { 'Array_' } else { '' }
29 | return '$arr$t.bname'
30 | }
31 |
32 | pub fn (t Type) name() string {
33 | arr := if t.is_array { '[]' } else { '' }
34 | return '$arr$t.tname'
35 | }
36 |
37 | pub fn (t Type) tname() string {
38 | arr := if t.is_array { 'Array_' } else { '' }
39 | return '$arr$t.tname'
40 | }
41 |
42 | pub struct Struct {
43 | pub:
44 | name string
45 | fields []StructField
46 | }
47 |
48 | pub struct StructField {
49 | pub:
50 | name string
51 | typ Type
52 | is_pub bool
53 | is_mut bool
54 | }
55 |
56 | pub struct SumType {
57 | pub:
58 | name string
59 | types []Type
60 | }
61 |
62 | pub struct Builtin {
63 | pub:
64 | cbase string
65 | }
--------------------------------------------------------------------------------
/util/write_error_message.v:
--------------------------------------------------------------------------------
1 | module util
2 |
3 | import error
4 |
5 | pub fn write_error_message(error error.Error, file string) {
6 | mut lines := file.split_into_lines()
7 | mut first_line := ''
8 | mut main_line := lines[error.pos.line_nr]
9 | mut last_line := ''
10 | if error.pos.line_nr > 0 {
11 | first_line = lines[error.pos.line_nr - 1]
12 | }
13 | if error.pos.line_nr + 1 < lines.len {
14 | last_line = lines[error.pos.line_nr + 1]
15 | }
16 | mut before := ' '
17 | eprintln('$error.pos.file:$error.pos.line_nr:$error.pos.char_nr: $error.level: $error.msg')
18 | if first_line != '' {
19 | before = calc_before(error.pos.line_nr - 1)
20 | eprintln('$before | $first_line')
21 | }
22 | before = calc_before(error.pos.line_nr)
23 | eprintln('$before | $main_line')
24 | eprint(' | ')
25 | for i in 0..error.pos.char_nr {
26 | if main_line[i] == `\t` {
27 | eprint('\t')
28 | } else {
29 | eprint(' ')
30 | }
31 | }
32 | for _ in 0..error.len {
33 | eprint('~')
34 | }
35 | eprintln('')
36 | if last_line != '' {
37 | before = calc_before(error.pos.line_nr + 1)
38 | eprintln('$before | $last_line')
39 | }
40 | }
41 |
42 | fn calc_before(i int) string {
43 | mut str := ''
44 | l := 5 - (i + 1).str().len
45 | for _ in 0..l {
46 | str += ' '
47 | }
48 | str += i.str()
49 | return str
50 | }
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | [](https://github.com/LouisSchmieder/lilV/actions/workflows/ci.yml)
2 |
3 | # lilV
4 | A small V compiler in V
5 |
6 | ## Why?!
7 | Because I'm bored and want to learn a bit more about compilers
8 |
9 | ## Process
10 | Windows isn't supported yet
11 |
12 | ### Parser
13 | The parser parses the following statements for now:
14 | - FunctionStmt
15 | - ModuleStmt
16 | - FunctionCallStmt
17 | - IfStmt
18 | - CommentStmt
19 | - ImportStmt
20 | - ReturnStmt
21 | - ConstStmt
22 | - DeclareStmt
23 | - StructStmt
24 | - SubtypeStmt
25 |
26 | And the following expressions:
27 | - StringExpr
28 | - NumberExpr
29 | - IdentExpr
30 | - CastExpr
31 |
32 | ### Gen
33 | The gen generates like in normal V C code, for now the code isn't completely valid, but this step is reached soon.
34 | Examples of C code you can see in `/tests` folder
35 |
36 | ### Fmt
37 | The fmt tool creates a V file from the ast which got parsed from the original file. It's an other concept of formatation atm.
38 | Examples of fmt'd files you can see in `/tests` folder (with this fileending `.v.fmted`)
39 |
40 | ### Ast
41 | For now the ast is printed too in the `/tests`. It's done that anyone can see the structure, so it's not important, just nice :)
42 |
43 | ## Contribution
44 | If you want to contribute please create an extra branch with your changes. Please add a file in `/tests` with the new feature and be sure that it compiles.
45 | Then just create a pull request. Thanks for the help
46 |
--------------------------------------------------------------------------------
/parser/fn.v:
--------------------------------------------------------------------------------
1 | module parser
2 |
3 | import ast
4 | import token
5 |
6 | fn (mut p Parser) function(is_pub bool, attrs []ast.Attribute, attrs_pos token.Position) ast.FunctionStmt {
7 | p.next()
8 | pos := p.pos()
9 | name := p.get_name()
10 | parameter := p.parameter()
11 | p.next()
12 | mut ret := p.get_type()
13 | stmts := p.parse_block()
14 | return ast.FunctionStmt{
15 | pos: pos
16 | is_pub: is_pub
17 | name: name
18 | ret: ret
19 | parameter: parameter
20 | attrs: attrs
21 | attrs_pos: attrs_pos
22 | mod: p.mod
23 | stmts: stmts
24 | }
25 | }
26 |
27 | fn (mut p Parser) parameter() []ast.Parameter {
28 | mut params := []ast.Parameter{}
29 | p.next()
30 | p.expect(.lpar)
31 | p.next()
32 | for p.tok.kind != .rpar {
33 | pos := p.tok.pos
34 | name := p.get_name()
35 | p.next()
36 | typ := p.get_type()
37 | params << ast.Parameter{
38 | pos: pos
39 | typ: typ
40 | name: name
41 | }
42 | if p.tok.kind != .comma {
43 | break
44 | }
45 | }
46 | p.expect(.rpar)
47 | return params
48 | }
49 |
50 | fn (mut p Parser) parse_function_call(pos token.Position, name string) ast.FunctionCallStmt {
51 | function := name.all_after_last('.')
52 | mod := name.all_before_last('.')
53 |
54 | p.expect(.lpar)
55 |
56 | mut parameter := []ast.Expr{}
57 |
58 | for {
59 | p.next()
60 | parameter << p.expr()
61 | if p.tok.kind != .comma {
62 | break
63 | }
64 | }
65 | p.expect(.rpar)
66 | p.next()
67 | return ast.FunctionCallStmt{
68 | name: function
69 | mod: mod
70 | pos: pos
71 | params: parameter
72 | }
73 | }
--------------------------------------------------------------------------------
/parser/attrs.v:
--------------------------------------------------------------------------------
1 | module parser
2 |
3 | import ast
4 | import token
5 |
6 | fn (mut p Parser) parse_attributes() ([]ast.Attribute, token.Position) {
7 | if p.tok.kind != .lsbr {
8 | return []ast.Attribute{}, p.pos()
9 | }
10 | pos := p.pos()
11 | mut attrs := []ast.Attribute{}
12 | for {
13 | p.next()
14 | attrs << p.parse_attribute()
15 | if p.tok.kind == .semicolon {
16 | continue
17 | } else {
18 | break
19 | }
20 | }
21 | p.expect(.rsbr)
22 | p.next()
23 | return attrs, pos
24 | }
25 |
26 | fn (mut p Parser) parse_attribute() ast.Attribute {
27 | mut name := ''
28 | mut kind := ast.AttribKind.name
29 | if p.tok.kind == .string {
30 | kind = .string
31 | name = p.tok.lit[1..p.tok.lit.len - 1]
32 | } else if p.tok.kind == .name {
33 | name = p.tok.lit
34 | } else if p.tok.kind == .number {
35 | name = p.tok.lit
36 | kind = .number
37 | } else {
38 | p.expect(.name)
39 | return ast.Attribute{}
40 | }
41 | p.next()
42 | if p.tok.kind != .colon {
43 | return ast.Attribute{
44 | name: name
45 | name_kind: kind
46 | has_arg: false
47 | }
48 | }
49 | p.next()
50 | mut arg := ''
51 | mut arg_kind := ast.AttribKind.name
52 | if p.tok.kind == .string {
53 | arg_kind = .string
54 | arg = p.tok.lit[1..p.tok.lit.len - 1]
55 | } else if p.tok.kind == .name {
56 | arg = p.tok.lit
57 | } else if p.tok.kind == .number {
58 | arg = p.tok.lit
59 | arg_kind = .number
60 | } else {
61 | p.expect(.name)
62 | return ast.Attribute{}
63 | }
64 | p.next()
65 | return ast.Attribute{
66 | name: name
67 | name_kind: kind
68 | has_arg: true
69 | arg: arg
70 | arg_kind: arg_kind
71 |
72 | }
73 | }
--------------------------------------------------------------------------------
/types/table.v:
--------------------------------------------------------------------------------
1 | module types
2 |
3 | pub struct Table {
4 | pub mut:
5 | types []Type
6 | type_idx map[string]int
7 | }
8 |
9 | pub fn create_default_table() &Table {
10 | mut table := &Table{
11 | }
12 | // add builtins
13 | table.register_builtin('i8', 'signed char', .builtin)
14 | table.register_builtin('i16', 'signed short', .builtin)
15 | table.register_builtin('int', 'signed long', .builtin)
16 | table.register_builtin('i64', 'signed long long', .builtin)
17 | table.register_builtin('byte', 'unsigned char', .builtin)
18 | table.register_builtin('u16', 'unsigned short', .builtin)
19 | table.register_builtin('u32', 'unsigned long', .builtin)
20 | table.register_builtin('u64', 'unsigned long long', .builtin)
21 | table.register_builtin('charptr', 'char *', .builtin)
22 | table.register_builtin('voidptr', 'void *', .builtin)
23 | table.register_builtin('void_', 'void', .builtin)
24 |
25 | return table
26 | }
27 |
28 | pub fn (mut table Table) register_type(mod string, name string, kind Kind, info Info) {
29 | typ := Type{
30 | mod: mod
31 | name: name
32 | tname: '${mod}.$name'
33 | bname: '${mod}__$name'
34 | kind: kind
35 | }
36 | table.types << typ
37 | table.type_idx['${mod}.$name'] = table.types.len - 1
38 | }
39 |
40 | pub fn (mut table Table) find_type(name string) ?Type {
41 | if name !in table.type_idx {
42 | return error('Unkown type `$name`')
43 | }
44 | return table.types[table.type_idx[name]]
45 | }
46 |
47 | pub fn (mut table Table) get_idx(name string) int {
48 | return table.type_idx[name]
49 | }
50 |
51 | fn (mut table Table) register_builtin(name string, cbase string, kind Kind) {
52 | typ := Type{
53 | mod: ''
54 | name: name
55 | tname: name
56 | bname: name
57 | kind: kind
58 | info: Builtin{
59 | cbase: cbase
60 | }
61 | }
62 | table.types << typ
63 | table.type_idx[name] = table.types.len - 1
64 | }
--------------------------------------------------------------------------------
/parser/struct.v:
--------------------------------------------------------------------------------
1 | module parser
2 |
3 | import token
4 | import ast
5 |
6 | fn (mut p Parser) parse_struct(ip bool, attrs []ast.Attribute, attrs_pos token.Position) ast.StructStmt {
7 | p.next()
8 | pos := p.pos()
9 | name := p.get_name()
10 | p.next()
11 | p.expect(.lcbr)
12 | p.next()
13 | // parse fields
14 | mut fields := []ast.StructField{}
15 | mut is_pub := false
16 | mut is_mut := false
17 | for p.tok.kind != .rcbr {
18 | if p.tok.kind != .name {
19 | if p.tok.kind == .key_pub {
20 | p.next()
21 | is_pub = true
22 | is_mut = false
23 | if p.tok.kind == .key_mut {
24 | p.next()
25 | is_mut = true
26 | }
27 | p.expect(.colon)
28 | } else if p.tok.kind == .key_mut {
29 | p.next()
30 | is_pub = false
31 | is_mut = true
32 | p.expect(.colon)
33 | } else {
34 | p.expect(.name)
35 | }
36 | p.next()
37 | }
38 | field_pos := p.pos()
39 | field_name := p.get_name()
40 | p.next()
41 | field_type := p.get_type()
42 | a, ap := p.parse_attributes()
43 | fields << ast.StructField {
44 | pos: field_pos
45 | name: field_name
46 | typ: field_type
47 | is_pub: is_pub
48 | is_mut: is_mut
49 | attrs: a
50 | attrs_pos: ap
51 | }
52 | }
53 | p.next()
54 | if p.table.get_idx(name) != 0 {
55 | p.errorp('Type `$name` already exists', pos, name.len)
56 | return ast.StructStmt{}
57 | }
58 | return ast.StructStmt {
59 | pos: pos
60 | is_pub: ip
61 | name: name
62 | attrs: attrs
63 | attrs_pos: attrs_pos
64 | fields: fields
65 | }
66 | }
67 |
68 | fn (mut p Parser) parse_sumtype(is_pub bool) ast.SumtypeStmt {
69 | p.next()
70 | p.next = false
71 | pos := p.pos()
72 | name := p.get_name()
73 | p.next()
74 | p.expect(.assign)
75 | p.next()
76 | mut names := []string{}
77 | for p.tok.kind == .name {
78 | names << p.get_name()
79 | p.next()
80 | if p.tok.kind != .pipe {
81 | break
82 | }
83 | p.next()
84 | }
85 | return ast.SumtypeStmt{
86 | pos: pos
87 | is_pub: is_pub
88 | name: name
89 | names: names
90 | }
91 | }
--------------------------------------------------------------------------------
/littleV.v:
--------------------------------------------------------------------------------
1 | module main
2 |
3 | import os
4 | import scanner
5 | import parser
6 | import fmt
7 | import util
8 | import gen
9 | import time
10 |
11 | const (
12 | tabs = '\t\t\t\t\t\t\t\t\t\t\t'
13 | )
14 |
15 | fn main() {
16 | debug('Testing files...', 0)
17 | files := os.ls('tests') or { panic(err) }
18 | for f in files {
19 | if f.ends_with('.v') {
20 | compile_file('./tests/$f')
21 | }
22 | }
23 | }
24 |
25 | fn compile_file(path string) {
26 | mut total := i64(0)
27 | debug('Testing `$path`', 1)
28 | data := os.read_file(path) or { '' }
29 | mut scan := scanner.create_scanner(data, path)
30 | mut pars := parser.create_parser(scan)
31 | debug('`$path` start parsing', 2)
32 | mut sw := time.new_stopwatch(auto_start: true)
33 | out, err := pars.parse_file()
34 | sw.stop()
35 | debug('`$path` finsh parsing (took ${sw.elapsed().microseconds()} ms)', 2)
36 | total += sw.elapsed().microseconds()
37 | debug('`$path` has $err.len problems', 2)
38 | if err.len > 0 {
39 | warns := err.filter(it.level == .warn)
40 | debug('`$path` has $warns.len warnings', 3)
41 | for warn in warns {
42 | util.write_error_message(warn, data)
43 | }
44 | errors := err.filter(it.level == .error)
45 | debug('`$path` has $errors.len errors', 3)
46 | for error in errors {
47 | util.write_error_message(error, data)
48 | }
49 | if errors.len > 0 {
50 | exit(1)
51 | }
52 | }
53 | os.write_file('${path}.pfile', '$out') or { panic(err) }
54 |
55 |
56 | // format
57 |
58 | debug('`$path` format ast', 2)
59 | mut f := fmt.create_fmt(out)
60 | debug('`$path` formatted ast', 2)
61 | sw.restart()
62 | res := f.format()
63 | sw.stop()
64 | is_formatted := if res != data { 'not ' } else {''}
65 | debug('`$path` is ${is_formatted}formatted (took ${sw.elapsed().microseconds()} ms)', 3)
66 | total += sw.elapsed().microseconds()
67 | os.write_file('${path}.fmted', '$res') or { panic(err) }
68 |
69 | debug('`$path` cgen starting', 2)
70 | mut g := gen.create_gen([&out])
71 | sw.restart()
72 | cr := g.gen()
73 | sw.stop()
74 | debug('`$path` cgen finished (took ${sw.elapsed().microseconds()} ms)', 2)
75 | total += sw.elapsed().microseconds()
76 | os.write_file('${path}.c', '$cr') or { panic(err) }
77 | debug('`$path` took to finish the job $total ms', 1)
78 | }
79 |
80 | fn debug(msg string, level int) {
81 | if level <= 0 {
82 | eprintln(msg)
83 | } else {
84 | t := tabs[..level]
85 | eprintln('$t$msg')
86 | }
87 | }
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | name: CI
2 |
3 | on: [push, pull_request]
4 |
5 | jobs:
6 | ubuntu:
7 | runs-on: ubuntu-latest
8 | steps:
9 | - name: Checkout V
10 | uses: actions/checkout@v2
11 | with:
12 | repository: vlang/v
13 | - name: Checkout lilV
14 | uses: actions/checkout@v2
15 | with:
16 | path: lilV
17 | - name: Build V
18 | run: |
19 | make
20 | sudo ./v symlink
21 | - name: Build lilV
22 | run: |
23 | cd lilV
24 | v .
25 | - name: Run tests
26 | run: |
27 | cd lilV
28 | ./lilV tests/functions.v
29 | macos:
30 | runs-on: macos-latest
31 | steps:
32 | - name: Checkout V
33 | uses: actions/checkout@v2
34 | with:
35 | repository: vlang/v
36 | - name: Checkout lilV
37 | uses: actions/checkout@v2
38 | with:
39 | path: lilV
40 | - name: Build V
41 | run: |
42 | make
43 | sudo ./v symlink
44 | - name: Build lilV
45 | run: |
46 | cd lilV
47 | v .
48 | - name: Run tests
49 | run: |
50 | cd lilV
51 | ./lilV tests/functions.v
52 | # windows-gcc:
53 | # runs-on: windows-latest
54 | # env:
55 | # VFLAGS: -cc gcc
56 | # steps:
57 | # - name: Checkout V
58 | # uses: actions/checkout@v2
59 | # with:
60 | # repository: vlang/v
61 | # - name: Checkout lilV
62 | # uses: actions/checkout@v2
63 | # with:
64 | # path: lilV
65 | # - name: Build V
66 | # run: |
67 | # ./make.bat -gcc
68 | # - name: Build lilV
69 | # run: |
70 | # .\v.exe lilV
71 | # - name: Run tests
72 | # run: |
73 | # cd lilV
74 | # .\lilV.exe tests/functions.v
75 | ubuntu-tcc:
76 | runs-on: ubuntu-latest
77 | env:
78 | VFLAGS: -cc tcc -cflags -bt10
79 | steps:
80 | - name: Checkout V
81 | uses: actions/checkout@v2
82 | with:
83 | repository: vlang/v
84 | - name: Checkout lilV
85 | uses: actions/checkout@v2
86 | with:
87 | path: lilV
88 | - name: Build V
89 | run: |
90 | echo $VFLAGS
91 | make
92 | sudo ./v symlink
93 | - name: Build lilV
94 | run: |
95 | cd lilV
96 | v .
97 | - name: Run tests
98 | run: |
99 | cd lilV
100 | ./lilV tests/functions.v
101 |
--------------------------------------------------------------------------------
/ast/ast.v:
--------------------------------------------------------------------------------
1 | module ast
2 |
3 | import token
4 | import types
5 |
6 | type Expr = StringExpr | NumberExpr | IdentExpr | Unknown | CastExpr
7 |
8 | type Stmt = FunctionStmt | ModuleStmt | FunctionCallStmt | IfStmt | CommentStmt | ImportStmt | Unknown | ReturnStmt | ConstStmt | DeclareStmt | StructStmt | IncludeStmt | FlagStmt | SumtypeStmt
9 |
10 | pub struct StringExpr {
11 | pub:
12 | pos token.Position
13 | str string
14 | }
15 |
16 | pub struct NumberExpr {
17 | pub:
18 | pos token.Position
19 | num string
20 | }
21 |
22 | pub struct IdentExpr {
23 | pub:
24 | pos token.Position
25 | expr Expr
26 | name string
27 | }
28 |
29 | pub struct FunctionStmt {
30 | pub:
31 | pos token.Position
32 | is_pub bool
33 | name string
34 | ret types.Type
35 | parameter []Parameter
36 | attrs_pos token.Position
37 | attrs []Attribute
38 | mod string
39 | stmts []Stmt
40 | }
41 |
42 | pub struct FunctionCallStmt {
43 | pub:
44 | pos token.Position
45 | mod string
46 | name string
47 | params []Expr
48 | }
49 |
50 | pub struct ModuleStmt {
51 | pub:
52 | pos token.Position
53 | name string
54 | }
55 |
56 | pub struct ImportStmt {
57 | pub:
58 | pos token.Position
59 | mod string
60 | has_as bool
61 | alias string
62 | }
63 |
64 | pub struct Parameter {
65 | pub:
66 | pos token.Position
67 | typ types.Type
68 | name string
69 | }
70 |
71 | pub struct IfStmt {
72 | pub:
73 | pos token.Position
74 | cond Expr
75 | stmts []Stmt
76 | elses []ElseStmt
77 | }
78 |
79 | pub struct ElseStmt {
80 | pub:
81 | pos token.Position
82 | has_cond bool
83 | cond Expr
84 | stmts []Stmt
85 | }
86 |
87 | pub struct CommentStmt {
88 | pub:
89 | pos token.Position
90 | multiline bool
91 | msg string
92 | }
93 |
94 | pub struct ReturnStmt {
95 | pub:
96 | pos token.Position
97 | expr Expr
98 | }
99 |
100 | pub struct CastExpr {
101 | pub:
102 | pos token.Position
103 | typ types.Type
104 | expr Expr
105 | }
106 |
107 | pub struct ConstStmt {
108 | pub:
109 | pos token.Position
110 | is_pub bool
111 | consts []Const
112 | }
113 |
114 | pub struct Const {
115 | pub:
116 | pos token.Position
117 | name string
118 | expr Expr
119 | }
120 |
121 | pub struct DeclareStmt {
122 | pub:
123 | pos token.Position
124 | name string
125 | expr Expr
126 | }
127 |
128 | pub struct StructStmt {
129 | pub:
130 | pos token.Position
131 | is_pub bool
132 | name string
133 | attrs []Attribute
134 | attrs_pos token.Position
135 | fields []StructField
136 | }
137 |
138 | pub struct StructField {
139 | pub:
140 | pos token.Position
141 | name string
142 | typ types.Type
143 | is_pub bool
144 | is_mut bool
145 | attrs []Attribute
146 | attrs_pos token.Position
147 | }
148 |
149 | pub struct IncludeStmt {
150 | pub:
151 | pos token.Position
152 | include string
153 | }
154 |
155 | pub struct SumtypeStmt {
156 | pub:
157 | pos token.Position
158 | is_pub bool
159 | name string
160 | names []string
161 | types []types.Type
162 | }
163 |
164 | pub struct FlagStmt {
165 | pos token.Position
166 | str string
167 | }
168 |
169 | pub struct Unknown{}
--------------------------------------------------------------------------------
/token/token.v:
--------------------------------------------------------------------------------
1 | module token
2 |
3 | pub struct Token {
4 | pub:
5 | kind Kind
6 | pos Position
7 | pub mut:
8 | lit string
9 | }
10 |
11 | pub enum Kind {
12 | unknown
13 | eof
14 | name
15 | number
16 | string
17 | str_inter // 'name=$user.name'
18 | chartoken // `A` - rune
19 | plus
20 | minus
21 | mul
22 | div
23 | mod
24 | xor
25 | pipe
26 | inc
27 | dec
28 | and
29 | logical_or
30 | not
31 | bit_not
32 | question
33 | comma
34 | semicolon
35 | colon
36 | arrow
37 | amp
38 | hash
39 | dollar
40 | at
41 | str_dollar
42 | left_shift
43 | right_shift
44 | not_in
45 | not_is
46 | assign
47 | decl_assign
48 | plus_assign
49 | minus_assign
50 | div_assign
51 | mult_assign
52 | xor_assign
53 | mod_assign
54 | or_assign
55 | and_assign
56 | right_shift_assign
57 | left_shift_assign
58 | lcbr
59 | rcbr
60 | lpar
61 | rpar
62 | lsbr
63 | rsbr
64 | eq
65 | ne
66 | gt
67 | lt
68 | ge
69 | le
70 | comment
71 | nl
72 | dot // .
73 | dotdot // ..
74 | ellipsis // ...
75 | keyword_beg
76 | key_as
77 | key_asm
78 | key_assert
79 | key_atomic
80 | key_break
81 | key_const
82 | key_continue
83 | key_defer
84 | key_else
85 | key_enum
86 | key_false
87 | key_for
88 | key_fn
89 | key_global
90 | key_go
91 | key_goto
92 | key_if
93 | key_import
94 | key_in
95 | key_interface
96 | key_is
97 | key_match
98 | key_module
99 | key_mut
100 | key_shared
101 | key_lock
102 | key_rlock
103 | key_none
104 | key_return
105 | key_select
106 | key_sizeof
107 | key_likely
108 | key_unlikely
109 | key_offsetof
110 | key_struct
111 | key_true
112 | key_type
113 | key_typeof
114 | key_dump
115 | key_orelse
116 | key_union
117 | key_pub
118 | key_static
119 | key_unsafe
120 | keyword_end
121 | _end_
122 | }
123 |
124 | pub const (
125 | keywords = map{
126 | 'as': Kind.key_as
127 | 'asm': Kind.key_asm
128 | 'assert': Kind.key_assert
129 | 'atomic': Kind.key_atomic
130 | 'break': Kind.key_break
131 | 'const': Kind.key_const
132 | 'continue': Kind.key_continue
133 | 'defer': Kind.key_defer
134 | 'else': Kind.key_else
135 | 'enum': Kind.key_enum
136 | 'false': Kind.key_false
137 | 'for': Kind.key_for
138 | 'fn': Kind.key_fn
139 | 'global': Kind.key_global
140 | 'go': Kind.key_go
141 | 'goto': Kind.key_goto
142 | 'if': Kind.key_if
143 | 'import': Kind.key_import
144 | 'in': Kind.key_in
145 | 'interface': Kind.key_interface
146 | 'is': Kind.key_is
147 | 'match': Kind.key_match
148 | 'module': Kind.key_module
149 | 'mut': Kind.key_mut
150 | 'shared': Kind.key_shared
151 | 'lock': Kind.key_lock
152 | 'rlock': Kind.key_rlock
153 | 'none': Kind.key_none
154 | 'return': Kind.key_return
155 | 'select': Kind.key_select
156 | 'sizeof': Kind.key_sizeof
157 | 'likely': Kind.key_likely
158 | 'unlikely': Kind.key_unlikely
159 | 'offsetof': Kind.key_offsetof
160 | 'struct': Kind.key_struct
161 | 'true': Kind.key_true
162 | 'type': Kind.key_type
163 | 'typeof': Kind.key_typeof
164 | 'dump': Kind.key_dump
165 | 'orelse': Kind.key_orelse
166 | 'union': Kind.key_union
167 | 'pub': Kind.key_pub
168 | 'static': Kind.key_static
169 | 'unsafe': Kind.key_unsafe
170 | }
171 | )
172 |
173 | pub fn create_token(kind Kind, file string, line_nr int, char_nr int, lit []byte) Token {
174 | return Token{
175 | kind: kind
176 | pos: Position{
177 | file: file
178 | line_nr: line_nr
179 | char_nr: char_nr
180 | }
181 | lit: string(lit)
182 | }
183 | }
184 |
--------------------------------------------------------------------------------
/tests/comments.v.pfile:
--------------------------------------------------------------------------------
1 | ast.File{
2 | mod: 'main'
3 | stmts: [ast.Stmt(ast.ModuleStmt{
4 | pos: token.Position{
5 | file: './tests/comments.v'
6 | line_nr: 0
7 | char_nr: 0
8 | }
9 | name: 'main'
10 | }), ast.Stmt(ast.ImportStmt{
11 | pos: token.Position{
12 | file: './tests/comments.v'
13 | line_nr: 2
14 | char_nr: 0
15 | }
16 | mod: 'test'
17 | has_as: true
18 | alias: 'abc'
19 | }), ast.Stmt(ast.CommentStmt{
20 | pos: token.Position{
21 | file: './tests/comments.v'
22 | line_nr: 4
23 | char_nr: 0
24 | }
25 | multiline: false
26 | msg: '// test
27 | '
28 | }), ast.Stmt(ast.CommentStmt{
29 | pos: token.Position{
30 | file: './tests/comments.v'
31 | line_nr: 5
32 | char_nr: 0
33 | }
34 | multiline: false
35 | msg: '/* test */'
36 | }), ast.Stmt(ast.FunctionStmt{
37 | pos: token.Position{
38 | file: './tests/comments.v'
39 | line_nr: 7
40 | char_nr: 7
41 | }
42 | is_pub: true
43 | name: 'main'
44 | ret: types.Type{
45 | mod: ''
46 | name: 'void_'
47 | tname: 'void_'
48 | bname: 'void_'
49 | kind: builtin
50 | info: types.Info(types.Builtin{
51 | cbase: 'void'
52 | })
53 | is_array: false
54 | }
55 | parameter: []
56 | attrs_pos: token.Position{
57 | file: './tests/comments.v'
58 | line_nr: 7
59 | char_nr: 0
60 | }
61 | attrs: []
62 | mod: 'main'
63 | stmts: []
64 | })]
65 | table: &types.Table{
66 | types: [types.Type{
67 | mod: ''
68 | name: 'i8'
69 | tname: 'i8'
70 | bname: 'i8'
71 | kind: builtin
72 | info: types.Info(types.Builtin{
73 | cbase: 'signed char'
74 | })
75 | is_array: false
76 | }, types.Type{
77 | mod: ''
78 | name: 'i16'
79 | tname: 'i16'
80 | bname: 'i16'
81 | kind: builtin
82 | info: types.Info(types.Builtin{
83 | cbase: 'signed short'
84 | })
85 | is_array: false
86 | }, types.Type{
87 | mod: ''
88 | name: 'int'
89 | tname: 'int'
90 | bname: 'int'
91 | kind: builtin
92 | info: types.Info(types.Builtin{
93 | cbase: 'signed long'
94 | })
95 | is_array: false
96 | }, types.Type{
97 | mod: ''
98 | name: 'i64'
99 | tname: 'i64'
100 | bname: 'i64'
101 | kind: builtin
102 | info: types.Info(types.Builtin{
103 | cbase: 'signed long long'
104 | })
105 | is_array: false
106 | }, types.Type{
107 | mod: ''
108 | name: 'byte'
109 | tname: 'byte'
110 | bname: 'byte'
111 | kind: builtin
112 | info: types.Info(types.Builtin{
113 | cbase: 'unsigned char'
114 | })
115 | is_array: false
116 | }, types.Type{
117 | mod: ''
118 | name: 'u16'
119 | tname: 'u16'
120 | bname: 'u16'
121 | kind: builtin
122 | info: types.Info(types.Builtin{
123 | cbase: 'unsigned short'
124 | })
125 | is_array: false
126 | }, types.Type{
127 | mod: ''
128 | name: 'u32'
129 | tname: 'u32'
130 | bname: 'u32'
131 | kind: builtin
132 | info: types.Info(types.Builtin{
133 | cbase: 'unsigned long'
134 | })
135 | is_array: false
136 | }, types.Type{
137 | mod: ''
138 | name: 'u64'
139 | tname: 'u64'
140 | bname: 'u64'
141 | kind: builtin
142 | info: types.Info(types.Builtin{
143 | cbase: 'unsigned long long'
144 | })
145 | is_array: false
146 | }, types.Type{
147 | mod: ''
148 | name: 'charptr'
149 | tname: 'charptr'
150 | bname: 'charptr'
151 | kind: builtin
152 | info: types.Info(types.Builtin{
153 | cbase: 'char *'
154 | })
155 | is_array: false
156 | }, types.Type{
157 | mod: ''
158 | name: 'voidptr'
159 | tname: 'voidptr'
160 | bname: 'voidptr'
161 | kind: builtin
162 | info: types.Info(types.Builtin{
163 | cbase: 'void *'
164 | })
165 | is_array: false
166 | }, types.Type{
167 | mod: ''
168 | name: 'void_'
169 | tname: 'void_'
170 | bname: 'void_'
171 | kind: builtin
172 | info: types.Info(types.Builtin{
173 | cbase: 'void'
174 | })
175 | is_array: false
176 | }]
177 | type_idx: {'i8': 0, 'i16': 1, 'int': 2, 'i64': 3, 'byte': 4, 'u16': 5, 'u32': 6, 'u64': 7, 'charptr': 8, 'voidptr': 9, 'void_': 10}
178 | }
179 | }
--------------------------------------------------------------------------------
/gen/gen.v:
--------------------------------------------------------------------------------
1 | module gen
2 |
3 | import strings
4 | import ast
5 | import error
6 | import types
7 |
8 | struct Gen {
9 | mut:
10 | builder strings.Builder
11 | headers strings.Builder
12 | functions strings.Builder
13 | consts strings.Builder
14 | types strings.Builder
15 | files []&ast.File
16 | file &ast.File
17 | errs []error.Error
18 | table &types.Table
19 | writer &strings.Builder
20 | }
21 |
22 | pub fn create_gen(files []&ast.File) &Gen {
23 | return &Gen{
24 | builder: strings.new_builder(4096)
25 | headers: strings.new_builder(256)
26 | functions: strings.new_builder(1024)
27 | types: strings.new_builder(1024)
28 | consts: strings.new_builder(1024)
29 | files: files
30 | errs: []error.Error{}
31 | file: 0
32 | table: 0
33 | writer: 0
34 | }
35 | }
36 |
37 | pub fn (mut g Gen) gen() string {
38 | for file in g.files {
39 | g.file = file
40 | g.table = file.table
41 | g.gen_file()
42 | }
43 | mut end := ''
44 | end += g.headers.str()
45 | end += g.types.str()
46 | end += g.consts.str()
47 | end += g.functions.str()
48 | end += g.builder.str()
49 | return end
50 | }
51 |
52 | fn (mut g Gen) gen_file() {
53 | g.gen_types()
54 | for stmt in g.file.stmts {
55 | g.stmt(stmt)
56 | }
57 | }
58 |
59 | fn (mut g Gen) gen_types() {
60 | for typ in g.table.types {
61 | g.twrite('typedef ')
62 | info := typ.info
63 | match info {
64 | types.Builtin {
65 | g.twrite(info.cbase)
66 | }
67 | types.Struct {
68 | g.twrite('struct {')
69 |
70 | g.twrite('}')
71 | }
72 | else {}
73 | }
74 | g.twriteln(' ${typ.bname()};')
75 | }
76 | }
77 |
78 | fn (mut g Gen) wrln(str string) {
79 | g.writer.writeln(str)
80 | }
81 |
82 | fn (mut g Gen) wr(str string) {
83 | g.writer.write_string(str)
84 | }
85 |
86 | fn (mut g Gen) write(str string) {
87 | unsafe { g.writer = &g.builder }
88 | g.wr(str)
89 | }
90 |
91 | fn (mut g Gen) writeln(str string) {
92 | unsafe { g.writer = &g.builder }
93 | g.wrln(str)
94 | }
95 |
96 | fn (mut g Gen) hwrite(str string) {
97 | unsafe { g.writer = &g.headers }
98 | g.wr(str)
99 | }
100 |
101 | fn (mut g Gen) hwriteln(str string) {
102 | unsafe { g.writer = &g.headers }
103 | g.wrln(str)
104 | }
105 |
106 | fn (mut g Gen) fwrite(str string) {
107 | unsafe { g.writer = &g.functions }
108 | g.wr(str)
109 | }
110 |
111 | fn (mut g Gen) fwriteln(str string) {
112 | unsafe { g.writer = &g.functions }
113 | g.wrln(str)
114 | }
115 |
116 | fn (mut g Gen) twrite(str string) {
117 | unsafe { g.writer = &g.types }
118 | g.wr(str)
119 | }
120 |
121 | fn (mut g Gen) twriteln(str string) {
122 | unsafe { g.writer = &g.types }
123 | g.wrln(str)
124 | }
125 |
126 | fn (mut g Gen) cwrite(str string) {
127 | unsafe { g.writer = &g.consts }
128 | g.wr(str)
129 | }
130 |
131 | fn (mut g Gen) cwriteln(str string) {
132 | unsafe { g.writer = &g.consts }
133 | g.wrln(str)
134 | }
135 |
136 | fn (mut g Gen) stmt(stmt ast.Stmt) {
137 | match stmt {
138 | ast.FunctionStmt{
139 | g.function_stmt(stmt)
140 | }
141 | ast.FunctionCallStmt {
142 | g.function_call_stmt(stmt)
143 | }
144 | ast.ReturnStmt{
145 | g.return_stmt(stmt)
146 | }
147 | ast.IfStmt {
148 | g.if_stmt(stmt)
149 | }
150 | ast.ConstStmt {
151 | g.const_stmt(stmt)
152 | }
153 | ast.DeclareStmt {
154 | g.decl_stmt(stmt)
155 | }
156 | else {}
157 | }
158 | }
159 |
160 | fn (mut g Gen) expr(expr ast.Expr) {
161 | match expr {
162 | ast.IdentExpr {
163 | g.wr('$expr.name')
164 | }
165 | ast.CastExpr {
166 | g.wr('((${expr.typ.bname()})')
167 | g.expr(expr.expr)
168 | g.wr(')')
169 | }
170 | ast.NumberExpr {
171 | g.wr('$expr.num')
172 | }
173 | ast.StringExpr {
174 | str := expr.str.all_before_last('\'')[1..]
175 | g.wr('"$str"')
176 | }
177 | else {}
178 | }
179 | }
180 |
181 | fn (mut g Gen) function_stmt(stmt ast.FunctionStmt) {
182 | name := '${stmt.mod}__$stmt.name'
183 | typ := stmt.ret.bname()
184 | mut line := '$typ ${name}('
185 |
186 | for i, p in stmt.parameter {
187 | line += '${p.typ.bname()} $p.name'
188 | if i > stmt.parameter.len - 1 {
189 | line += ', '
190 | }
191 | }
192 | line += ')'
193 | g.fwriteln('$line;')
194 | g.writeln('$line {')
195 |
196 | for s in stmt.stmts {
197 | g.stmt(s)
198 | }
199 |
200 | g.writeln('}')
201 | }
202 |
203 | fn (mut g Gen) function_call_stmt(stmt ast.FunctionCallStmt) {
204 | name := '${stmt.mod}__$stmt.name'
205 | g.write('${name}(')
206 | for expr in stmt.params {
207 | g.expr(expr)
208 | }
209 | g.writeln(');')
210 | }
211 |
212 | fn (mut g Gen) return_stmt(stmt ast.ReturnStmt) {
213 | g.write('return ')
214 | g.expr(stmt.expr)
215 | g.writeln(';')
216 | }
217 |
218 | fn (mut g Gen) if_stmt(stmt ast.IfStmt) {
219 | g.write('if (')
220 | g.expr(stmt.cond)
221 | g.writeln('){')
222 | for s in stmt.stmts {
223 | g.stmt(s)
224 | }
225 | for el in stmt.elses {
226 | g.else_stmt(el)
227 | }
228 | g.writeln('}')
229 | }
230 |
231 | fn (mut g Gen) else_stmt(stmt ast.ElseStmt) {
232 | g.write('} else')
233 | if stmt.has_cond {
234 | g.write(' if(')
235 | g.expr(stmt.cond)
236 | g.write(')')
237 | }
238 | g.writeln(' {')
239 | for s in stmt.stmts {
240 | g.stmt(s)
241 | }
242 | }
243 |
244 | fn (mut g Gen) const_stmt(stmt ast.ConstStmt) {
245 | for c in stmt.consts {
246 | name := '${g.file.mod}__$c.name'
247 | g.cwrite('#define $name ')
248 | g.expr(c.expr)
249 | g.cwriteln('')
250 | }
251 | }
252 |
253 | fn (mut g Gen) decl_stmt(stmt ast.DeclareStmt) {
254 | typ := g.file.get_type(stmt.expr).bname()
255 | g.write('$typ $stmt.name = ')
256 | g.expr(stmt.expr)
257 | g.writeln(';')
258 | }
--------------------------------------------------------------------------------
/tests/consts.v.pfile:
--------------------------------------------------------------------------------
1 | ast.File{
2 | mod: 'main'
3 | stmts: [ast.Stmt(ast.ModuleStmt{
4 | pos: token.Position{
5 | file: './tests/consts.v'
6 | line_nr: 0
7 | char_nr: 0
8 | }
9 | name: 'main'
10 | }), ast.Stmt(ast.ConstStmt{
11 | pos: token.Position{
12 | file: './tests/consts.v'
13 | line_nr: 2
14 | char_nr: 4
15 | }
16 | is_pub: true
17 | consts: [ast.Const{
18 | pos: token.Position{
19 | file: './tests/consts.v'
20 | line_nr: 2
21 | char_nr: 10
22 | }
23 | name: 'abc'
24 | expr: ast.Expr(ast.StringExpr{
25 | pos: token.Position{
26 | file: './tests/consts.v'
27 | line_nr: 2
28 | char_nr: 16
29 | }
30 | str: ''def''
31 | })
32 | }]
33 | }), ast.Stmt(ast.ConstStmt{
34 | pos: token.Position{
35 | file: './tests/consts.v'
36 | line_nr: 3
37 | char_nr: 0
38 | }
39 | is_pub: false
40 | consts: [ast.Const{
41 | pos: token.Position{
42 | file: './tests/consts.v'
43 | line_nr: 4
44 | char_nr: 1
45 | }
46 | name: 'test'
47 | expr: ast.Expr(ast.NumberExpr{
48 | pos: token.Position{
49 | file: './tests/consts.v'
50 | line_nr: 4
51 | char_nr: 8
52 | }
53 | num: '123'
54 | })
55 | }, ast.Const{
56 | pos: token.Position{
57 | file: './tests/consts.v'
58 | line_nr: 5
59 | char_nr: 1
60 | }
61 | name: 'abc'
62 | expr: ast.Expr(ast.NumberExpr{
63 | pos: token.Position{
64 | file: './tests/consts.v'
65 | line_nr: 5
66 | char_nr: 7
67 | }
68 | num: '544'
69 | })
70 | }]
71 | })]
72 | table: &types.Table{
73 | types: [types.Type{
74 | mod: ''
75 | name: 'i8'
76 | tname: 'i8'
77 | bname: 'i8'
78 | kind: builtin
79 | info: types.Info(types.Builtin{
80 | cbase: 'signed char'
81 | })
82 | is_array: false
83 | }, types.Type{
84 | mod: ''
85 | name: 'i16'
86 | tname: 'i16'
87 | bname: 'i16'
88 | kind: builtin
89 | info: types.Info(types.Builtin{
90 | cbase: 'signed short'
91 | })
92 | is_array: false
93 | }, types.Type{
94 | mod: ''
95 | name: 'int'
96 | tname: 'int'
97 | bname: 'int'
98 | kind: builtin
99 | info: types.Info(types.Builtin{
100 | cbase: 'signed long'
101 | })
102 | is_array: false
103 | }, types.Type{
104 | mod: ''
105 | name: 'i64'
106 | tname: 'i64'
107 | bname: 'i64'
108 | kind: builtin
109 | info: types.Info(types.Builtin{
110 | cbase: 'signed long long'
111 | })
112 | is_array: false
113 | }, types.Type{
114 | mod: ''
115 | name: 'byte'
116 | tname: 'byte'
117 | bname: 'byte'
118 | kind: builtin
119 | info: types.Info(types.Builtin{
120 | cbase: 'unsigned char'
121 | })
122 | is_array: false
123 | }, types.Type{
124 | mod: ''
125 | name: 'u16'
126 | tname: 'u16'
127 | bname: 'u16'
128 | kind: builtin
129 | info: types.Info(types.Builtin{
130 | cbase: 'unsigned short'
131 | })
132 | is_array: false
133 | }, types.Type{
134 | mod: ''
135 | name: 'u32'
136 | tname: 'u32'
137 | bname: 'u32'
138 | kind: builtin
139 | info: types.Info(types.Builtin{
140 | cbase: 'unsigned long'
141 | })
142 | is_array: false
143 | }, types.Type{
144 | mod: ''
145 | name: 'u64'
146 | tname: 'u64'
147 | bname: 'u64'
148 | kind: builtin
149 | info: types.Info(types.Builtin{
150 | cbase: 'unsigned long long'
151 | })
152 | is_array: false
153 | }, types.Type{
154 | mod: ''
155 | name: 'charptr'
156 | tname: 'charptr'
157 | bname: 'charptr'
158 | kind: builtin
159 | info: types.Info(types.Builtin{
160 | cbase: 'char *'
161 | })
162 | is_array: false
163 | }, types.Type{
164 | mod: ''
165 | name: 'voidptr'
166 | tname: 'voidptr'
167 | bname: 'voidptr'
168 | kind: builtin
169 | info: types.Info(types.Builtin{
170 | cbase: 'void *'
171 | })
172 | is_array: false
173 | }, types.Type{
174 | mod: ''
175 | name: 'void_'
176 | tname: 'void_'
177 | bname: 'void_'
178 | kind: builtin
179 | info: types.Info(types.Builtin{
180 | cbase: 'void'
181 | })
182 | is_array: false
183 | }]
184 | type_idx: {'i8': 0, 'i16': 1, 'int': 2, 'i64': 3, 'byte': 4, 'u16': 5, 'u32': 6, 'u64': 7, 'charptr': 8, 'voidptr': 9, 'void_': 10}
185 | }
186 | }
--------------------------------------------------------------------------------
/parser/parser.v:
--------------------------------------------------------------------------------
1 | module parser
2 |
3 | import scanner
4 | import error
5 | import token
6 | import types
7 | import ast
8 |
9 | struct Parser {
10 | mut:
11 | s &scanner.Scanner
12 | table &types.Table
13 | err []error.Error
14 | tok token.Token
15 | stmts []ast.Stmt
16 | mod string
17 | next bool
18 | }
19 |
20 | pub fn create_parser(s &scanner.Scanner) &Parser {
21 | return &Parser{
22 | s: s
23 | err: []error.Error{}
24 | stmts: []ast.Stmt{}
25 | table: types.create_default_table()
26 | }
27 | }
28 |
29 | pub fn (mut p Parser) parse_file() (ast.File, []error.Error) {
30 | p.next()
31 | p.parse_module()
32 | p.parse_imports()
33 |
34 |
35 | for p.tok.kind != .eof {
36 | p.next = true
37 | p.parse_top_stmt()
38 | if p.next {
39 | p.next()
40 | }
41 | }
42 |
43 | return ast.File{
44 | stmts: p.stmts
45 | table: p.table
46 | mod: p.mod
47 | }, p.err
48 | }
49 |
50 | fn (mut p Parser) parse_module() {
51 | mut name := 'main'
52 | pos := p.pos()
53 | if p.tok.kind == .key_module {
54 | p.next()
55 | name = p.get_name()
56 | }
57 | p.next()
58 | p.mod = name
59 | p.stmts << ast.ModuleStmt{
60 | pos: pos
61 | name: name
62 | }
63 | }
64 |
65 | fn (mut p Parser) parse_imports() {
66 | for p.tok.kind == .key_import {
67 | pos := p.tok.pos
68 | p.next()
69 | mod := p.get_name()
70 | p.next()
71 | if p.tok.kind != .key_as {
72 | p.stmts << ast.ImportStmt{
73 | pos: pos
74 | mod: mod
75 | has_as: false
76 | }
77 | continue
78 | }
79 | p.next()
80 | alias := p.get_name()
81 | p.stmts << ast.ImportStmt{
82 | pos: pos
83 | mod: mod
84 | has_as: true
85 | alias: alias
86 | }
87 | p.next()
88 | }
89 | }
90 |
91 | fn (mut p Parser) parse_top_stmt() {
92 | mut is_pub := false
93 | tmp := p.tok
94 | attrs, attrs_pos := p.parse_attributes()
95 | if p.tok.kind == .key_pub {
96 | is_pub = true
97 | p.next()
98 | }
99 | if p.tok.kind !in [.key_fn, .key_enum, .key_struct] {
100 | if attrs.len > 0 {
101 | tmp_2 := p.tok
102 | p.tok = tmp
103 | p.error('Unexpected attribute')
104 | p.tok = tmp_2
105 | }
106 | }
107 | match p.tok.kind {
108 | .key_fn {
109 | p.stmts << p.function(is_pub, attrs, attrs_pos)
110 | }
111 | .key_struct {
112 | p.stmts << p.parse_struct(is_pub, attrs, attrs_pos)
113 | }
114 | .key_const {
115 | p.stmts << p.consts(is_pub)
116 | }
117 | .key_type {
118 | p.stmts << p.parse_sumtype(is_pub)
119 | }
120 | else {
121 | p.error('Unexpected top level stmt: `$p.tok.lit`')
122 | }
123 | }
124 | }
125 |
126 | fn (mut p Parser) parse_stmt() ?ast.Stmt {
127 | mut stmt := ast.Stmt(ast.Unknown{})
128 | match p.tok.kind {
129 | .name {
130 | pos := p.tok.pos
131 | mut name := p.tok.lit
132 | p.next()
133 | match p.tok.kind {
134 | .lpar {
135 | stmt = p.parse_function_call(pos, name)
136 | }
137 | .decl_assign {
138 | stmt = p.decl_stmt(pos, name)
139 | }
140 | else {}
141 | }
142 | }
143 | .key_if {
144 | stmt = p.parse_if()
145 | }
146 | .key_return {
147 | pos := p.pos()
148 | p.next()
149 | expr := p.expr()
150 | stmt = ast.ReturnStmt{
151 | pos: pos
152 | expr: expr
153 | }
154 | }
155 | else {
156 | return error('Unknown statement $p.tok.kind')
157 | }
158 | }
159 |
160 | return stmt
161 | }
162 |
163 | fn (mut p Parser) expr() ast.Expr {
164 | pos := p.tok.pos
165 | lit := p.tok.lit
166 | match p.tok.kind {
167 | .string {
168 | p.next()
169 | return ast.StringExpr{
170 | pos: pos
171 | str: lit
172 | }
173 | }
174 | .number {
175 | p.next()
176 | return ast.NumberExpr{
177 | pos: pos
178 | num: lit
179 | }
180 | }
181 | .name {
182 | typ := p.get_name()
183 | if p.table.get_idx(typ) > 0 {
184 | p.next()
185 | p.expect(.lpar)
186 | p.next()
187 | expr := p.expr()
188 | p.expect(.rpar)
189 | p.next()
190 | t := p.table.find_type(typ) or {
191 | p.error(err.msg)
192 | types.Type{}
193 | }
194 | return ast.CastExpr{
195 | pos: pos
196 | typ: t
197 | expr: expr
198 | }
199 | }
200 | p.next()
201 | return ast.IdentExpr{
202 | pos: pos
203 | name: lit
204 | }
205 | }
206 | else {
207 | p.next()
208 | return ast.StringExpr{}
209 | }
210 | }
211 | }
212 |
213 | fn (mut p Parser) parse_block() []ast.Stmt {
214 | p.expect(.lcbr)
215 | p.next()
216 | mut stmts := []ast.Stmt{}
217 | for {
218 | stmts << p.parse_stmt() or {
219 | break
220 | }
221 | }
222 | p.expect(.rcbr)
223 | return stmts
224 | }
225 |
226 | fn (mut p Parser) next() {
227 | p.tok = p.s.scan()
228 | if p.tok.kind == .comment {
229 | p.stmts << ast.CommentStmt{
230 | pos: p.tok.pos
231 | msg: p.tok.lit
232 | multiline: p.tok.lit.split_into_lines().len > 1
233 | }
234 | p.next()
235 | }
236 | }
237 |
238 | fn (mut p Parser) get_name() string {
239 | if p.expect(.name) {
240 | return p.tok.lit
241 | }
242 | return ''
243 | }
244 |
245 | fn (mut p Parser) get_type() types.Type {
246 | mut lit := ''
247 | if p.tok.kind != .name && p.tok.kind != .lsbr {
248 | typ := p.table.find_type('void_') or {
249 | p.error(err.msg)
250 | return types.Type{}
251 | }
252 | return typ
253 | }
254 | mut is_arr := false
255 | if p.tok.kind == .lsbr {
256 | p.next()
257 | p.expect(.rsbr)
258 | p.next()
259 | is_arr = true
260 | }
261 | lit += p.tok.lit
262 | tmp := p.tok
263 | p.next()
264 | mut typ := p.table.find_type(lit) or {
265 | tmp_2 := p.tok
266 | p.tok = tmp
267 | p.tok.lit = lit
268 | p.error(err.msg)
269 | p.tok = tmp_2
270 | return types.Type{}
271 | }
272 | if is_arr {
273 | typ.set_array()
274 | }
275 | return typ
276 | }
277 |
278 | fn (mut p Parser) expect(kind token.Kind) bool {
279 | if p.tok.kind != kind {
280 | p.error('Unexpected token `$p.tok.lit` expected `$kind`')
281 | return false
282 | }
283 | return true
284 | }
285 |
286 | fn (mut p Parser) pos() token.Position {
287 | return p.tok.pos
288 | }
289 |
290 | fn (mut p Parser) error(msg string) {
291 | p.err << error.Error{
292 | pos: p.tok.pos
293 | len: p.tok.lit.len
294 | level: .error
295 | msg: msg
296 | }
297 | }
298 |
299 | fn (mut p Parser) errorp(msg string, pos token.Position, len int) {
300 | p.err << error.Error{
301 | pos: pos
302 | len: len
303 | level: .error
304 | msg: msg
305 | }
306 | }
--------------------------------------------------------------------------------
/tests/if.v.pfile:
--------------------------------------------------------------------------------
1 | ast.File{
2 | mod: 'main'
3 | stmts: [ast.Stmt(ast.ModuleStmt{
4 | pos: token.Position{
5 | file: './tests/if.v'
6 | line_nr: 0
7 | char_nr: 0
8 | }
9 | name: 'main'
10 | }), ast.Stmt(ast.FunctionStmt{
11 | pos: token.Position{
12 | file: './tests/if.v'
13 | line_nr: 2
14 | char_nr: 3
15 | }
16 | is_pub: false
17 | name: 'main'
18 | ret: types.Type{
19 | mod: ''
20 | name: 'void_'
21 | tname: 'void_'
22 | bname: 'void_'
23 | kind: builtin
24 | info: types.Info(types.Builtin{
25 | cbase: 'void'
26 | })
27 | is_array: false
28 | }
29 | parameter: []
30 | attrs_pos: token.Position{
31 | file: './tests/if.v'
32 | line_nr: 2
33 | char_nr: 0
34 | }
35 | attrs: []
36 | mod: 'main'
37 | stmts: [ast.Stmt(ast.IfStmt{
38 | pos: token.Position{
39 | file: './tests/if.v'
40 | line_nr: 3
41 | char_nr: 1
42 | }
43 | cond: ast.Expr(ast.IdentExpr{
44 | pos: token.Position{
45 | file: './tests/if.v'
46 | line_nr: 3
47 | char_nr: 4
48 | }
49 | expr: unknown sum type value
50 | name: 'ab'
51 | })
52 | stmts: [ast.Stmt(ast.FunctionCallStmt{
53 | pos: token.Position{
54 | file: './tests/if.v'
55 | line_nr: 4
56 | char_nr: 2
57 | }
58 | mod: 'eprintln'
59 | name: 'eprintln'
60 | params: [ast.Expr(ast.StringExpr{
61 | pos: token.Position{
62 | file: './tests/if.v'
63 | line_nr: 4
64 | char_nr: 11
65 | }
66 | str: ''test''
67 | })]
68 | })]
69 | elses: [ast.ElseStmt{
70 | pos: token.Position{
71 | file: './tests/if.v'
72 | line_nr: 5
73 | char_nr: 3
74 | }
75 | has_cond: true
76 | cond: ast.Expr(ast.IdentExpr{
77 | pos: token.Position{
78 | file: './tests/if.v'
79 | line_nr: 5
80 | char_nr: 11
81 | }
82 | expr: unknown sum type value
83 | name: 'c'
84 | })
85 | stmts: [ast.Stmt(ast.FunctionCallStmt{
86 | pos: token.Position{
87 | file: './tests/if.v'
88 | line_nr: 6
89 | char_nr: 2
90 | }
91 | mod: 'eprintln'
92 | name: 'eprintln'
93 | params: [ast.Expr(ast.StringExpr{
94 | pos: token.Position{
95 | file: './tests/if.v'
96 | line_nr: 6
97 | char_nr: 11
98 | }
99 | str: ''abc''
100 | })]
101 | })]
102 | }, ast.ElseStmt{
103 | pos: token.Position{
104 | file: './tests/if.v'
105 | line_nr: 7
106 | char_nr: 3
107 | }
108 | has_cond: false
109 | cond: ast.Expr(ast.Unknown{})
110 | stmts: [ast.Stmt(ast.FunctionCallStmt{
111 | pos: token.Position{
112 | file: './tests/if.v'
113 | line_nr: 8
114 | char_nr: 2
115 | }
116 | mod: 'eprintln'
117 | name: 'eprintln'
118 | params: [ast.Expr(ast.NumberExpr{
119 | pos: token.Position{
120 | file: './tests/if.v'
121 | line_nr: 8
122 | char_nr: 11
123 | }
124 | num: '123'
125 | })]
126 | })]
127 | }]
128 | })]
129 | })]
130 | table: &types.Table{
131 | types: [types.Type{
132 | mod: ''
133 | name: 'i8'
134 | tname: 'i8'
135 | bname: 'i8'
136 | kind: builtin
137 | info: types.Info(types.Builtin{
138 | cbase: 'signed char'
139 | })
140 | is_array: false
141 | }, types.Type{
142 | mod: ''
143 | name: 'i16'
144 | tname: 'i16'
145 | bname: 'i16'
146 | kind: builtin
147 | info: types.Info(types.Builtin{
148 | cbase: 'signed short'
149 | })
150 | is_array: false
151 | }, types.Type{
152 | mod: ''
153 | name: 'int'
154 | tname: 'int'
155 | bname: 'int'
156 | kind: builtin
157 | info: types.Info(types.Builtin{
158 | cbase: 'signed long'
159 | })
160 | is_array: false
161 | }, types.Type{
162 | mod: ''
163 | name: 'i64'
164 | tname: 'i64'
165 | bname: 'i64'
166 | kind: builtin
167 | info: types.Info(types.Builtin{
168 | cbase: 'signed long long'
169 | })
170 | is_array: false
171 | }, types.Type{
172 | mod: ''
173 | name: 'byte'
174 | tname: 'byte'
175 | bname: 'byte'
176 | kind: builtin
177 | info: types.Info(types.Builtin{
178 | cbase: 'unsigned char'
179 | })
180 | is_array: false
181 | }, types.Type{
182 | mod: ''
183 | name: 'u16'
184 | tname: 'u16'
185 | bname: 'u16'
186 | kind: builtin
187 | info: types.Info(types.Builtin{
188 | cbase: 'unsigned short'
189 | })
190 | is_array: false
191 | }, types.Type{
192 | mod: ''
193 | name: 'u32'
194 | tname: 'u32'
195 | bname: 'u32'
196 | kind: builtin
197 | info: types.Info(types.Builtin{
198 | cbase: 'unsigned long'
199 | })
200 | is_array: false
201 | }, types.Type{
202 | mod: ''
203 | name: 'u64'
204 | tname: 'u64'
205 | bname: 'u64'
206 | kind: builtin
207 | info: types.Info(types.Builtin{
208 | cbase: 'unsigned long long'
209 | })
210 | is_array: false
211 | }, types.Type{
212 | mod: ''
213 | name: 'charptr'
214 | tname: 'charptr'
215 | bname: 'charptr'
216 | kind: builtin
217 | info: types.Info(types.Builtin{
218 | cbase: 'char *'
219 | })
220 | is_array: false
221 | }, types.Type{
222 | mod: ''
223 | name: 'voidptr'
224 | tname: 'voidptr'
225 | bname: 'voidptr'
226 | kind: builtin
227 | info: types.Info(types.Builtin{
228 | cbase: 'void *'
229 | })
230 | is_array: false
231 | }, types.Type{
232 | mod: ''
233 | name: 'void_'
234 | tname: 'void_'
235 | bname: 'void_'
236 | kind: builtin
237 | info: types.Info(types.Builtin{
238 | cbase: 'void'
239 | })
240 | is_array: false
241 | }]
242 | type_idx: {'i8': 0, 'i16': 1, 'int': 2, 'i64': 3, 'byte': 4, 'u16': 5, 'u32': 6, 'u64': 7, 'charptr': 8, 'voidptr': 9, 'void_': 10}
243 | }
244 | }
--------------------------------------------------------------------------------
/tests/functions.v.pfile:
--------------------------------------------------------------------------------
1 | ast.File{
2 | mod: 'test'
3 | stmts: [ast.Stmt(ast.ModuleStmt{
4 | pos: token.Position{
5 | file: './tests/functions.v'
6 | line_nr: 0
7 | char_nr: 0
8 | }
9 | name: 'test'
10 | }), ast.Stmt(ast.FunctionStmt{
11 | pos: token.Position{
12 | file: './tests/functions.v'
13 | line_nr: 2
14 | char_nr: 3
15 | }
16 | is_pub: false
17 | name: 'test'
18 | ret: types.Type{
19 | mod: ''
20 | name: 'int'
21 | tname: 'int'
22 | bname: 'int'
23 | kind: builtin
24 | info: types.Info(types.Builtin{
25 | cbase: 'signed long'
26 | })
27 | is_array: false
28 | }
29 | parameter: [ast.Parameter{
30 | pos: token.Position{
31 | file: './tests/functions.v'
32 | line_nr: 2
33 | char_nr: 9
34 | }
35 | typ: types.Type{
36 | mod: ''
37 | name: 'i8'
38 | tname: 'i8'
39 | bname: 'i8'
40 | kind: builtin
41 | info: types.Info(types.Builtin{
42 | cbase: 'signed char'
43 | })
44 | is_array: false
45 | }
46 | name: 'test'
47 | }]
48 | attrs_pos: token.Position{
49 | file: './tests/functions.v'
50 | line_nr: 2
51 | char_nr: 0
52 | }
53 | attrs: []
54 | mod: 'test'
55 | stmts: [ast.Stmt(ast.ReturnStmt{
56 | pos: token.Position{
57 | file: './tests/functions.v'
58 | line_nr: 3
59 | char_nr: 1
60 | }
61 | expr: ast.Expr(ast.CastExpr{
62 | pos: token.Position{
63 | file: './tests/functions.v'
64 | line_nr: 3
65 | char_nr: 8
66 | }
67 | typ: types.Type{
68 | mod: ''
69 | name: 'int'
70 | tname: 'int'
71 | bname: 'int'
72 | kind: builtin
73 | info: types.Info(types.Builtin{
74 | cbase: 'signed long'
75 | })
76 | is_array: false
77 | }
78 | expr: ast.Expr(ast.IdentExpr{
79 | pos: token.Position{
80 | file: './tests/functions.v'
81 | line_nr: 3
82 | char_nr: 12
83 | }
84 | expr: unknown sum type value
85 | name: 'test'
86 | })
87 | })
88 | })]
89 | }), ast.Stmt(ast.FunctionStmt{
90 | pos: token.Position{
91 | file: './tests/functions.v'
92 | line_nr: 7
93 | char_nr: 3
94 | }
95 | is_pub: false
96 | name: 'main'
97 | ret: types.Type{
98 | mod: ''
99 | name: 'void_'
100 | tname: 'void_'
101 | bname: 'void_'
102 | kind: builtin
103 | info: types.Info(types.Builtin{
104 | cbase: 'void'
105 | })
106 | is_array: false
107 | }
108 | parameter: []
109 | attrs_pos: token.Position{
110 | file: './tests/functions.v'
111 | line_nr: 6
112 | char_nr: 0
113 | }
114 | attrs: [ast.Attribute{
115 | name: 'test'
116 | name_kind: string
117 | has_arg: true
118 | arg: '123'
119 | arg_kind: number
120 | }, ast.Attribute{
121 | name: 'abc'
122 | name_kind: name
123 | has_arg: false
124 | arg: ''
125 | arg_kind: string
126 | }]
127 | mod: 'test'
128 | stmts: [ast.Stmt(ast.DeclareStmt{
129 | pos: token.Position{
130 | file: './tests/functions.v'
131 | line_nr: 8
132 | char_nr: 1
133 | }
134 | name: 'abc'
135 | expr: ast.Expr(ast.NumberExpr{
136 | pos: token.Position{
137 | file: './tests/functions.v'
138 | line_nr: 8
139 | char_nr: 8
140 | }
141 | num: '123'
142 | })
143 | }), ast.Stmt(ast.FunctionCallStmt{
144 | pos: token.Position{
145 | file: './tests/functions.v'
146 | line_nr: 9
147 | char_nr: 1
148 | }
149 | mod: 'test'
150 | name: 'test'
151 | params: [ast.Expr(ast.IdentExpr{
152 | pos: token.Position{
153 | file: './tests/functions.v'
154 | line_nr: 9
155 | char_nr: 7
156 | }
157 | expr: unknown sum type value
158 | name: 'abc'
159 | })]
160 | })]
161 | })]
162 | table: &types.Table{
163 | types: [types.Type{
164 | mod: ''
165 | name: 'i8'
166 | tname: 'i8'
167 | bname: 'i8'
168 | kind: builtin
169 | info: types.Info(types.Builtin{
170 | cbase: 'signed char'
171 | })
172 | is_array: false
173 | }, types.Type{
174 | mod: ''
175 | name: 'i16'
176 | tname: 'i16'
177 | bname: 'i16'
178 | kind: builtin
179 | info: types.Info(types.Builtin{
180 | cbase: 'signed short'
181 | })
182 | is_array: false
183 | }, types.Type{
184 | mod: ''
185 | name: 'int'
186 | tname: 'int'
187 | bname: 'int'
188 | kind: builtin
189 | info: types.Info(types.Builtin{
190 | cbase: 'signed long'
191 | })
192 | is_array: false
193 | }, types.Type{
194 | mod: ''
195 | name: 'i64'
196 | tname: 'i64'
197 | bname: 'i64'
198 | kind: builtin
199 | info: types.Info(types.Builtin{
200 | cbase: 'signed long long'
201 | })
202 | is_array: false
203 | }, types.Type{
204 | mod: ''
205 | name: 'byte'
206 | tname: 'byte'
207 | bname: 'byte'
208 | kind: builtin
209 | info: types.Info(types.Builtin{
210 | cbase: 'unsigned char'
211 | })
212 | is_array: false
213 | }, types.Type{
214 | mod: ''
215 | name: 'u16'
216 | tname: 'u16'
217 | bname: 'u16'
218 | kind: builtin
219 | info: types.Info(types.Builtin{
220 | cbase: 'unsigned short'
221 | })
222 | is_array: false
223 | }, types.Type{
224 | mod: ''
225 | name: 'u32'
226 | tname: 'u32'
227 | bname: 'u32'
228 | kind: builtin
229 | info: types.Info(types.Builtin{
230 | cbase: 'unsigned long'
231 | })
232 | is_array: false
233 | }, types.Type{
234 | mod: ''
235 | name: 'u64'
236 | tname: 'u64'
237 | bname: 'u64'
238 | kind: builtin
239 | info: types.Info(types.Builtin{
240 | cbase: 'unsigned long long'
241 | })
242 | is_array: false
243 | }, types.Type{
244 | mod: ''
245 | name: 'charptr'
246 | tname: 'charptr'
247 | bname: 'charptr'
248 | kind: builtin
249 | info: types.Info(types.Builtin{
250 | cbase: 'char *'
251 | })
252 | is_array: false
253 | }, types.Type{
254 | mod: ''
255 | name: 'voidptr'
256 | tname: 'voidptr'
257 | bname: 'voidptr'
258 | kind: builtin
259 | info: types.Info(types.Builtin{
260 | cbase: 'void *'
261 | })
262 | is_array: false
263 | }, types.Type{
264 | mod: ''
265 | name: 'void_'
266 | tname: 'void_'
267 | bname: 'void_'
268 | kind: builtin
269 | info: types.Info(types.Builtin{
270 | cbase: 'void'
271 | })
272 | is_array: false
273 | }]
274 | type_idx: {'i8': 0, 'i16': 1, 'int': 2, 'i64': 3, 'byte': 4, 'u16': 5, 'u32': 6, 'u64': 7, 'charptr': 8, 'voidptr': 9, 'void_': 10}
275 | }
276 | }
--------------------------------------------------------------------------------
/fmt/fmt.v:
--------------------------------------------------------------------------------
1 | module fmt
2 |
3 | import ast
4 | import strings
5 | import error
6 | import token
7 |
8 | const (
9 | tabs = '\t\t\t\t\t\t\t\t\t\t\t'
10 | max_types_per_line = 6
11 | )
12 |
13 | pub struct Fmt {
14 | mut:
15 | builder strings.Builder
16 | level int
17 | file &ast.File
18 | errs []error.Error
19 | last_pos token.Position
20 | pos token.Position
21 | next_nl int
22 | }
23 |
24 | pub fn create_fmt(file &ast.File) &Fmt {
25 | return &Fmt {
26 | builder: strings.new_builder(1024)
27 | level: 0
28 | file: file
29 | errs: []error.Error{}
30 | next_nl: 0
31 | }
32 | }
33 |
34 | pub fn (mut f Fmt) format() string {
35 | for stmt in f.file.stmts {
36 | f.stmt(stmt)
37 | }
38 | f.builder.write_string('\n')
39 | return f.builder.str()
40 | }
41 |
42 | fn (mut f Fmt) up() {
43 | f.level++
44 | }
45 |
46 | fn (mut f Fmt) down() {
47 | f.level--
48 | }
49 |
50 | fn (mut f Fmt) nl() {
51 | f.next_nl++
52 | }
53 |
54 | fn (mut f Fmt) write(s string, data ...bool) {
55 | if (f.pos.line_nr > f.last_pos.line_nr) || (data.len > 0 && data[0]) {
56 | f.builder.write_string('\n'.repeat(f.next_nl + 1))
57 | f.builder.write_string(tabs[..f.level])
58 | f.next_nl = 0
59 | }
60 | f.builder.write_string(s)
61 | f.last_pos = f.pos
62 | }
63 |
64 | fn (mut f Fmt) stmt(stmt ast.Stmt) {
65 | match stmt {
66 | ast.ModuleStmt {
67 | f.module_stmt(stmt)
68 | }
69 | ast.CommentStmt {
70 | f.comment_stmt(stmt)
71 | }
72 | ast.ImportStmt {
73 | f.import_stmt(stmt)
74 | }
75 | ast.FunctionStmt {
76 | f.function_stmt(stmt)
77 | }
78 | ast.FunctionCallStmt {
79 | f.function_call_stmt(stmt)
80 | }
81 | ast.IfStmt {
82 | f.if_stmt(stmt)
83 | }
84 | ast.ConstStmt {
85 | f.const_stmt(stmt)
86 | }
87 | ast.StructStmt {
88 | f.struct_stmt(stmt)
89 | }
90 | ast.SumtypeStmt {
91 | f.sumtype_stmt(stmt)
92 | }
93 | else {}
94 | }
95 | }
96 |
97 | fn (mut f Fmt) expr(expr ast.Expr) {
98 | match expr {
99 | ast.StringExpr {
100 | f.string_expr(expr)
101 | }
102 | ast.NumberExpr {
103 | f.number_expr(expr)
104 | }
105 | ast.IdentExpr {
106 | f.ident_expr(expr)
107 | }
108 | else {}
109 | }
110 | }
111 |
112 | fn (mut f Fmt) module_stmt(stmt ast.ModuleStmt) {
113 | f.pos = stmt.pos
114 | f.write('module $stmt.name')
115 | f.nl()
116 | }
117 |
118 | fn (mut f Fmt) comment_stmt(stmt ast.CommentStmt) {
119 | f.pos = stmt.pos
120 | mut msg := stmt.msg.replace_each(['/*', '', '*/', '', '//', '']).trim_space()
121 | if !stmt.multiline {
122 | f.write('// $msg')
123 | }
124 | }
125 |
126 | fn (mut f Fmt) import_stmt(stmt ast.ImportStmt) {
127 | f.pos = stmt.pos
128 | f.write('import $stmt.mod')
129 | if stmt.has_as {
130 | f.write(' as $stmt.alias')
131 | }
132 | f.nl()
133 | }
134 |
135 | fn (mut f Fmt) attributes(attrs []ast.Attribute, pos token.Position) {
136 | f.pos = pos
137 | f.write('[')
138 | for i, attr in attrs {
139 | match attr.name_kind {
140 | .string {
141 | f.write('\'$attr.name\'')
142 | }
143 | else {
144 | f.write('$attr.name')
145 | }
146 | }
147 | if attr.has_arg {
148 | match attr.arg_kind {
149 | .string {
150 | f.write(': \'$attr.arg\'')
151 | }
152 | else {
153 | f.write(': $attr.arg')
154 | }
155 | }
156 | }
157 | if i < attrs.len - 1 {
158 | f.write('; ')
159 | }
160 | }
161 | f.write(']')
162 | }
163 |
164 | fn (mut f Fmt) function_stmt(stmt ast.FunctionStmt) {
165 | if stmt.attrs.len > 0 {
166 | f.attributes(stmt.attrs, stmt.attrs_pos)
167 | }
168 | f.pos = stmt.pos
169 | if stmt.is_pub {
170 | f.write('pub ')
171 | }
172 | f.write('fn $stmt.name ')
173 | f.write('(')
174 | for i, param in stmt.parameter {
175 | f.write('$param.name ${param.typ.name()}')
176 | if i < stmt.parameter.len - 1 {
177 | f.write(', ')
178 | }
179 | }
180 | f.write(')')
181 |
182 | if f.file.table.get_idx(stmt.ret.name) != 10 {
183 | f.write(' ${stmt.ret.name()}')
184 | }
185 |
186 | f.write(' {')
187 | f.up()
188 |
189 | for s in stmt.stmts {
190 | f.stmt(s)
191 | }
192 |
193 | f.down()
194 | f.pos = stmt.pos
195 | f.write('}', stmt.stmts.len > 0)
196 | f.nl()
197 | }
198 |
199 | fn (mut f Fmt) function_call_stmt(stmt ast.FunctionCallStmt) {
200 | f.pos = stmt.pos
201 | if stmt.mod != f.file.mod {
202 | f.write('${stmt.mod}.')
203 | }
204 | f.write('${stmt.name} (')
205 |
206 | for i, expr in stmt.params {
207 | f.expr(expr)
208 | if i > stmt.params.len - 1 {
209 | f.write(', ')
210 | }
211 | }
212 |
213 | f.write(')')
214 | }
215 |
216 | fn (mut f Fmt) if_stmt(stmt ast.IfStmt) {
217 | f.pos = stmt.pos
218 | f.write('if ')
219 | f.expr(stmt.cond)
220 | f.write(' {')
221 | f.up()
222 | for s in stmt.stmts {
223 | f.stmt(s)
224 | }
225 | f.down()
226 | for el in stmt.elses {
227 | f.else_stmt(el)
228 | }
229 | f.write('} ', true)
230 | }
231 |
232 | fn (mut f Fmt) else_stmt(stmt ast.ElseStmt) {
233 | f.pos = stmt.pos
234 | f.write('} else')
235 | if stmt.has_cond {
236 | f.write(' if ')
237 | f.expr(stmt.cond)
238 | }
239 | f.write(' {')
240 | f.up()
241 | for s in stmt.stmts {
242 | f.stmt(s)
243 | }
244 | f.down()
245 | }
246 |
247 | fn (mut f Fmt) const_stmt(stmt ast.ConstStmt) {
248 | f.pos = stmt.pos
249 | if stmt.is_pub {
250 | f.write('pub ')
251 | }
252 | f.write('const ')
253 | one_line := stmt.consts.len == 1
254 | if !one_line {
255 | f.write('(')
256 | f.up()
257 | defer {
258 | f.down()
259 | f.write('\n)')
260 | }
261 | }
262 |
263 | mut longest_name := 0
264 |
265 | for c in stmt.consts {
266 | if c.name.len > longest_name {
267 | longest_name = c.name.len
268 | }
269 | }
270 |
271 | longest_name += 1
272 |
273 | for c in stmt.consts {
274 | if !one_line {
275 | f.pos = c.pos
276 | }
277 | spaces := ' '.repeat(longest_name - c.name.len)
278 | f.write('$c.name$spaces= ')
279 | f.expr(c.expr)
280 | }
281 |
282 | f.nl()
283 | }
284 |
285 | fn (mut f Fmt) struct_stmt(stmt ast.StructStmt) {
286 | if stmt.attrs.len > 0 {
287 | f.attributes(stmt.attrs, stmt.attrs_pos)
288 | }
289 | f.pos = stmt.pos
290 | if stmt.is_pub {
291 | f.write('pub ')
292 | }
293 | f.write('struct $stmt.name {')
294 | mut longest_name := 0
295 | mut longest_type := 0
296 | for field in stmt.fields {
297 | if field.name.len > longest_name {
298 | longest_name = field.name.len
299 | }
300 | if field.typ.name().len > longest_type {
301 | longest_type = field.typ.name().len
302 | }
303 | }
304 |
305 | longest_name += 1
306 | longest_type += 1
307 |
308 | prnm := stmt.fields.filter(!it.is_pub && !it.is_mut) // private not mutable
309 | prm := stmt.fields.filter(!it.is_pub && it.is_mut) // private mutable
310 | pnm := stmt.fields.filter(it.is_pub && !it.is_mut) // public not mutable
311 | pm := stmt.fields.filter(it.is_pub && it.is_mut) // public mutable
312 | f.up()
313 | if prnm.len > 0 {
314 | for field in prnm {
315 | f.pos = field.pos
316 | nspaces := ' '.repeat(longest_name - field.name.len)
317 | tspaces := ' '.repeat(longest_type - field.typ.name().len)
318 | f.write('$field.name$nspaces${field.typ.name()}$tspaces')
319 | if field.attrs.len > 0 {
320 | f.attributes(field.attrs, field.attrs_pos)
321 | }
322 | }
323 | }
324 | if prm.len > 0 {
325 | f.down()
326 | f.write('mut:', true)
327 | f.up()
328 | for field in prm {
329 | f.pos = field.pos
330 | nspaces := ' '.repeat(longest_name - field.name.len)
331 | tspaces := ' '.repeat(longest_type - field.typ.name().len)
332 | f.write('$field.name$nspaces${field.typ.name()}$tspaces')
333 | if field.attrs.len > 0 {
334 | f.attributes(field.attrs, field.attrs_pos)
335 | }
336 | }
337 | }
338 | if pnm.len > 0 {
339 | f.down()
340 | f.write('pub:', true)
341 | f.up()
342 | for field in pnm {
343 | f.pos = field.pos
344 | nspaces := ' '.repeat(longest_name - field.name.len)
345 | tspaces := ' '.repeat(longest_type - field.typ.name().len)
346 | f.write('$field.name$nspaces${field.typ.name()}$tspaces')
347 | if field.attrs.len > 0 {
348 | f.attributes(field.attrs, field.attrs_pos)
349 | }
350 | }
351 | }
352 | if pm.len > 0 {
353 | f.down()
354 | f.write('pub mut:', true)
355 | f.up()
356 | for field in pm {
357 | f.pos = field.pos
358 | nspaces := ' '.repeat(longest_name - field.name.len)
359 | tspaces := ' '.repeat(longest_type - field.typ.name().len)
360 | f.write('$field.name$nspaces${field.typ.name()}$tspaces')
361 | if field.attrs.len > 0 {
362 | f.attributes(field.attrs, field.attrs_pos)
363 | }
364 | }
365 | }
366 |
367 |
368 | f.down()
369 | }
370 |
371 | fn (mut f Fmt) sumtype_stmt(stmt ast.SumtypeStmt) {
372 | f.pos = stmt.pos
373 | mut len := 0
374 | if stmt.is_pub {
375 | f.write('pub ')
376 | len += 4
377 | }
378 | f.write('type $stmt.name =')
379 | len += 7
380 | len += stmt.name.len
381 | mut str := ''
382 | for i, name in stmt.names {
383 | str += ' $name '
384 | if i < stmt.names.len - 1 {
385 | str += '|'
386 | if i % max_types_per_line == 0 && i > 0 {
387 | str += '\n'
388 | str += ' '.repeat(len)
389 | }
390 | }
391 | }
392 | f.write(str)
393 | f.write('', true)
394 | }
395 |
396 | fn (mut f Fmt) string_expr(expr ast.StringExpr) {
397 | f.write(expr.str)
398 | }
399 |
400 | fn (mut f Fmt) number_expr(expr ast.NumberExpr) {
401 | f.write(expr.num)
402 | }
403 |
404 | fn (mut f Fmt) ident_expr(expr ast.IdentExpr) {
405 | f.write(expr.name)
406 | }
407 |
--------------------------------------------------------------------------------
/tests/structs.v.pfile:
--------------------------------------------------------------------------------
1 | ast.File{
2 | mod: 'main'
3 | stmts: [ast.Stmt(ast.ModuleStmt{
4 | pos: token.Position{
5 | file: './tests/structs.v'
6 | line_nr: 0
7 | char_nr: 0
8 | }
9 | name: 'main'
10 | }), ast.Stmt(ast.SumtypeStmt{
11 | pos: token.Position{
12 | file: './tests/structs.v'
13 | line_nr: 2
14 | char_nr: 9
15 | }
16 | is_pub: true
17 | name: 'ABC'
18 | names: ['byte', 'int', 'Name', 'Test', 'abc', 'string', 'u64', 'test2']
19 | types: []
20 | }), ast.Stmt(ast.StructStmt{
21 | pos: token.Position{
22 | file: './tests/structs.v'
23 | line_nr: 5
24 | char_nr: 7
25 | }
26 | is_pub: false
27 | name: 'Name'
28 | attrs: [ast.Attribute{
29 | name: 'abc'
30 | name_kind: name
31 | has_arg: false
32 | arg: ''
33 | arg_kind: string
34 | }]
35 | attrs_pos: token.Position{
36 | file: './tests/structs.v'
37 | line_nr: 4
38 | char_nr: 0
39 | }
40 | fields: [ast.StructField{
41 | pos: token.Position{
42 | file: './tests/structs.v'
43 | line_nr: 6
44 | char_nr: 1
45 | }
46 | name: 'test'
47 | typ: types.Type{
48 | mod: ''
49 | name: 'u32'
50 | tname: 'u32'
51 | bname: 'u32'
52 | kind: builtin
53 | info: types.Info(types.Builtin{
54 | cbase: 'unsigned long'
55 | })
56 | is_array: false
57 | }
58 | is_pub: false
59 | is_mut: false
60 | attrs: []
61 | attrs_pos: token.Position{
62 | file: './tests/structs.v'
63 | line_nr: 7
64 | char_nr: 0
65 | }
66 | }, ast.StructField{
67 | pos: token.Position{
68 | file: './tests/structs.v'
69 | line_nr: 8
70 | char_nr: 1
71 | }
72 | name: 'field'
73 | typ: types.Type{
74 | mod: ''
75 | name: 'byte'
76 | tname: 'byte'
77 | bname: 'byte'
78 | kind: builtin
79 | info: types.Info(types.Builtin{
80 | cbase: 'unsigned char'
81 | })
82 | is_array: false
83 | }
84 | is_pub: false
85 | is_mut: true
86 | attrs: [ast.Attribute{
87 | name: 'help'
88 | name_kind: name
89 | has_arg: false
90 | arg: ''
91 | arg_kind: string
92 | }]
93 | attrs_pos: token.Position{
94 | file: './tests/structs.v'
95 | line_nr: 8
96 | char_nr: 12
97 | }
98 | }, ast.StructField{
99 | pos: token.Position{
100 | file: './tests/structs.v'
101 | line_nr: 9
102 | char_nr: 1
103 | }
104 | name: 'nums'
105 | typ: types.Type{
106 | mod: ''
107 | name: 'int'
108 | tname: 'int'
109 | bname: 'int'
110 | kind: builtin
111 | info: types.Info(types.Builtin{
112 | cbase: 'signed long'
113 | })
114 | is_array: true
115 | }
116 | is_pub: false
117 | is_mut: true
118 | attrs: []
119 | attrs_pos: token.Position{
120 | file: './tests/structs.v'
121 | line_nr: 10
122 | char_nr: 0
123 | }
124 | }, ast.StructField{
125 | pos: token.Position{
126 | file: './tests/structs.v'
127 | line_nr: 11
128 | char_nr: 1
129 | }
130 | name: 'abc'
131 | typ: types.Type{
132 | mod: ''
133 | name: 'int'
134 | tname: 'int'
135 | bname: 'int'
136 | kind: builtin
137 | info: types.Info(types.Builtin{
138 | cbase: 'signed long'
139 | })
140 | is_array: false
141 | }
142 | is_pub: true
143 | is_mut: false
144 | attrs: []
145 | attrs_pos: token.Position{
146 | file: './tests/structs.v'
147 | line_nr: 12
148 | char_nr: 0
149 | }
150 | }, ast.StructField{
151 | pos: token.Position{
152 | file: './tests/structs.v'
153 | line_nr: 13
154 | char_nr: 1
155 | }
156 | name: 'cd'
157 | typ: types.Type{
158 | mod: ''
159 | name: 'u64'
160 | tname: 'u64'
161 | bname: 'u64'
162 | kind: builtin
163 | info: types.Info(types.Builtin{
164 | cbase: 'unsigned long long'
165 | })
166 | is_array: false
167 | }
168 | is_pub: true
169 | is_mut: true
170 | attrs: []
171 | attrs_pos: token.Position{
172 | file: './tests/structs.v'
173 | line_nr: 14
174 | char_nr: 0
175 | }
176 | }]
177 | })]
178 | table: &types.Table{
179 | types: [types.Type{
180 | mod: ''
181 | name: 'i8'
182 | tname: 'i8'
183 | bname: 'i8'
184 | kind: builtin
185 | info: types.Info(types.Builtin{
186 | cbase: 'signed char'
187 | })
188 | is_array: false
189 | }, types.Type{
190 | mod: ''
191 | name: 'i16'
192 | tname: 'i16'
193 | bname: 'i16'
194 | kind: builtin
195 | info: types.Info(types.Builtin{
196 | cbase: 'signed short'
197 | })
198 | is_array: false
199 | }, types.Type{
200 | mod: ''
201 | name: 'int'
202 | tname: 'int'
203 | bname: 'int'
204 | kind: builtin
205 | info: types.Info(types.Builtin{
206 | cbase: 'signed long'
207 | })
208 | is_array: false
209 | }, types.Type{
210 | mod: ''
211 | name: 'i64'
212 | tname: 'i64'
213 | bname: 'i64'
214 | kind: builtin
215 | info: types.Info(types.Builtin{
216 | cbase: 'signed long long'
217 | })
218 | is_array: false
219 | }, types.Type{
220 | mod: ''
221 | name: 'byte'
222 | tname: 'byte'
223 | bname: 'byte'
224 | kind: builtin
225 | info: types.Info(types.Builtin{
226 | cbase: 'unsigned char'
227 | })
228 | is_array: false
229 | }, types.Type{
230 | mod: ''
231 | name: 'u16'
232 | tname: 'u16'
233 | bname: 'u16'
234 | kind: builtin
235 | info: types.Info(types.Builtin{
236 | cbase: 'unsigned short'
237 | })
238 | is_array: false
239 | }, types.Type{
240 | mod: ''
241 | name: 'u32'
242 | tname: 'u32'
243 | bname: 'u32'
244 | kind: builtin
245 | info: types.Info(types.Builtin{
246 | cbase: 'unsigned long'
247 | })
248 | is_array: false
249 | }, types.Type{
250 | mod: ''
251 | name: 'u64'
252 | tname: 'u64'
253 | bname: 'u64'
254 | kind: builtin
255 | info: types.Info(types.Builtin{
256 | cbase: 'unsigned long long'
257 | })
258 | is_array: false
259 | }, types.Type{
260 | mod: ''
261 | name: 'charptr'
262 | tname: 'charptr'
263 | bname: 'charptr'
264 | kind: builtin
265 | info: types.Info(types.Builtin{
266 | cbase: 'char *'
267 | })
268 | is_array: false
269 | }, types.Type{
270 | mod: ''
271 | name: 'voidptr'
272 | tname: 'voidptr'
273 | bname: 'voidptr'
274 | kind: builtin
275 | info: types.Info(types.Builtin{
276 | cbase: 'void *'
277 | })
278 | is_array: false
279 | }, types.Type{
280 | mod: ''
281 | name: 'void_'
282 | tname: 'void_'
283 | bname: 'void_'
284 | kind: builtin
285 | info: types.Info(types.Builtin{
286 | cbase: 'void'
287 | })
288 | is_array: false
289 | }]
290 | type_idx: {'i8': 0, 'i16': 1, 'int': 2, 'i64': 3, 'byte': 4, 'u16': 5, 'u32': 6, 'u64': 7, 'charptr': 8, 'voidptr': 9, 'void_': 10}
291 | }
292 | }
--------------------------------------------------------------------------------
/scanner/scanner.v:
--------------------------------------------------------------------------------
1 | module scanner
2 |
3 | import token
4 |
5 | struct Scanner {
6 | data []byte
7 | filename string
8 | mut:
9 | pos int
10 | line_nr int
11 | char_nr int
12 | lit []byte
13 | }
14 |
15 | pub fn create_scanner(input string, filename string) &Scanner {
16 | return &Scanner{
17 | data: input.bytes()
18 | filename: filename
19 | pos: 0
20 | line_nr: 0
21 | char_nr: 0
22 | lit: []byte{len: 1}
23 | }
24 | }
25 |
26 | pub fn (mut s Scanner) scan() token.Token {
27 | s.lit = []byte{len: 1}
28 | char_nr := s.char_nr
29 | line_nr := s.line_nr
30 | mut c, _ := s.next(false)
31 |
32 | if c == 0 {
33 | return token.create_token(.eof, s.filename, line_nr, s.char_nr, s.lit)
34 | }
35 | match c {
36 | `+` {
37 | if c == s.th_next() {
38 | s.next(true)
39 | return token.create_token(.inc, s.filename, line_nr, char_nr, s.lit)
40 | }
41 | if s.th_next() == `=` {
42 | s.next(true)
43 | return token.create_token(.plus_assign, s.filename, line_nr, char_nr, s.lit)
44 | }
45 | return token.create_token(.plus, s.filename, line_nr, char_nr, s.lit)
46 | }
47 | `-` {
48 | if c == s.th_next() {
49 | s.next(true)
50 | return token.create_token(.dec, s.filename, line_nr, char_nr, s.lit)
51 | }
52 | if s.th_next() == `=` {
53 | s.next(true)
54 | return token.create_token(.plus_assign, s.filename, line_nr, char_nr, s.lit)
55 | }
56 | return token.create_token(.minus, s.filename, line_nr, char_nr, s.lit)
57 | }
58 | `*` {
59 | if s.th_next() == `=` {
60 | s.next(true)
61 | return token.create_token(.mult_assign, s.filename, line_nr, char_nr, s.lit)
62 | }
63 | return token.create_token(.mul, s.filename, line_nr, char_nr, s.lit)
64 | }
65 | `/` {
66 | if s.th_next() == `=` {
67 | s.next(true)
68 | return token.create_token(.div_assign, s.filename, line_nr, char_nr, s.lit)
69 | }
70 | if s.th_next() == `/` {
71 | // line comment
72 | for {
73 | t, _ := s.next(true, true)
74 | if t == `\n` {
75 | break
76 | }
77 | }
78 | return token.create_token(.comment, s.filename, line_nr, char_nr, s.lit)
79 | }
80 | if s.th_next() == `*` {
81 | mut last := byte(0x00)
82 | for {
83 | t, _ := s.next(true, true)
84 | if last == `*` && t == `/` {
85 | break
86 | }
87 | last = t
88 | }
89 | l := s.lit
90 | s.pos--
91 | s.char_nr--
92 | s.next(false)
93 | return token.create_token(.comment, s.filename, line_nr, char_nr, l)
94 | }
95 | return token.create_token(.div, s.filename, line_nr, char_nr, s.lit)
96 | }
97 | `%` {
98 | if s.th_next() == `=` {
99 | s.next(true)
100 | return token.create_token(.mod_assign, s.filename, line_nr, char_nr, s.lit)
101 | }
102 | return token.create_token(.mod, s.filename, line_nr, char_nr, s.lit)
103 | }
104 | `^` {
105 | if s.th_next() == `=` {
106 | s.next(true)
107 | return token.create_token(.xor_assign, s.filename, line_nr, char_nr, s.lit)
108 | }
109 | return token.create_token(.xor, s.filename, line_nr, char_nr, s.lit)
110 | }
111 | `|` {
112 | if c == s.th_next() {
113 | s.next(true)
114 | return token.create_token(.logical_or, s.filename, line_nr, char_nr, s.lit)
115 | }
116 | if s.th_next() == `=` {
117 | s.next(true)
118 | return token.create_token(.or_assign, s.filename, line_nr, char_nr, s.lit)
119 | }
120 | return token.create_token(.pipe, s.filename, line_nr, char_nr, s.lit)
121 | }
122 | `&` {
123 | if c == s.th_next() {
124 | s.next(true)
125 | return token.create_token(.and, s.filename, line_nr, char_nr, s.lit)
126 | }
127 | if s.th_next() == `=` {
128 | s.next(true)
129 | return token.create_token(.and_assign, s.filename, line_nr, char_nr, s.lit)
130 | }
131 | return token.create_token(.amp, s.filename, line_nr, char_nr, s.lit)
132 | }
133 | `:` {
134 | if s.th_next() == `=` {
135 | s.next(true)
136 | return token.create_token(.decl_assign, s.filename, line_nr, char_nr, s.lit)
137 | }
138 | return token.create_token(.colon, s.filename, line_nr, char_nr, s.lit)
139 | }
140 | `!` {
141 | if s.th_next() == `=` {
142 | s.next(true)
143 | return token.create_token(.ne, s.filename, line_nr, char_nr, s.lit)
144 | }
145 | if s.th_next() == `i` {
146 | s.next(true)
147 | if s.th_next() == `s` {
148 | return token.create_token(.not_is, s.filename, line_nr, char_nr, s.lit)
149 | } else if s.th_next() == `n` {
150 | return token.create_token(.not_in, s.filename, line_nr, char_nr, s.lit)
151 | }
152 | }
153 | return token.create_token(.not, s.filename, line_nr, char_nr, s.lit)
154 | }
155 | `'`, `"` {
156 | ch := c
157 | for {
158 | c, _ = s.next(true, true)
159 | if c == ch {
160 | break
161 | }
162 | }
163 | return token.create_token(.string, s.filename, line_nr, char_nr, s.lit)
164 | }
165 | `=` {
166 | if c == s.th_next() {
167 | return token.create_token(.eq, s.filename, line_nr, char_nr, s.lit)
168 | }
169 | return token.create_token(.assign, s.filename, line_nr, char_nr, s.lit)
170 | }
171 | `>` {
172 | if s.th_next() == c {
173 | s.next(true)
174 | if s.th_next() == `=` {
175 | s.next(true)
176 | return token.create_token(.right_shift_assign, s.filename, line_nr, char_nr, s.lit)
177 | }
178 | return token.create_token(.right_shift, s.filename, line_nr, char_nr, s.lit)
179 | }
180 | if s.th_next() == `=` {
181 | return token.create_token(.ge, s.filename, line_nr, char_nr, s.lit)
182 | }
183 | return token.create_token(.gt, s.filename, line_nr, char_nr, s.lit)
184 | }
185 | `<` {
186 | if s.th_next() == c {
187 | s.next(true)
188 | if s.th_next() == `=` {
189 | s.next(true)
190 | return token.create_token(.left_shift_assign, s.filename, line_nr, char_nr, s.lit)
191 | }
192 | return token.create_token(.left_shift, s.filename, line_nr, char_nr, s.lit)
193 | }
194 | if s.th_next() == `=` {
195 | s.next(true)
196 | return token.create_token(.le, s.filename, line_nr, char_nr, s.lit)
197 | }
198 | if s.th_next() == `-` {
199 | s.next(true)
200 | return token.create_token(.arrow, s.filename, line_nr, char_nr, s.lit)
201 | }
202 | return token.create_token(.lt, s.filename, line_nr, char_nr, s.lit)
203 | }
204 | `~` {
205 | return token.create_token(.bit_not, s.filename, line_nr, char_nr, s.lit)
206 | }
207 | `?` {
208 | return token.create_token(.question, s.filename, line_nr, char_nr, s.lit)
209 | }
210 | `,` {
211 | return token.create_token(.comma, s.filename, line_nr, char_nr, s.lit)
212 | }
213 | `;` {
214 | return token.create_token(.semicolon, s.filename, line_nr, char_nr, s.lit)
215 | }
216 | `#` {
217 | return token.create_token(.hash, s.filename, line_nr, char_nr, s.lit)
218 | }
219 | `$` {
220 | return token.create_token(.dollar, s.filename, line_nr, char_nr, s.lit)
221 | }
222 | `@` {
223 | return token.create_token(.at, s.filename, line_nr, char_nr, s.lit)
224 | }
225 | `{` {
226 | return token.create_token(.lcbr, s.filename, line_nr, char_nr, s.lit)
227 | }
228 | `(` {
229 | return token.create_token(.lpar, s.filename, line_nr, char_nr, s.lit)
230 | }
231 | `[` {
232 | return token.create_token(.lsbr, s.filename, line_nr, char_nr, s.lit)
233 | }
234 | `}` {
235 | return token.create_token(.rcbr, s.filename, line_nr, char_nr, s.lit)
236 | }
237 | `)` {
238 | return token.create_token(.rpar, s.filename, line_nr, char_nr, s.lit)
239 | }
240 | `]` {
241 | return token.create_token(.rsbr, s.filename, line_nr, char_nr, s.lit)
242 | }
243 | `.` {
244 | if s.th_next() == c {
245 | s.next(true)
246 | if s.th_next() == c {
247 | s.next(true)
248 | return token.create_token(.ellipsis, s.filename, line_nr, char_nr, s.lit)
249 | }
250 | return token.create_token(.dotdot, s.filename, line_nr, char_nr, s.lit)
251 | }
252 | return token.create_token(.dot, s.filename, line_nr, char_nr, s.lit)
253 | }
254 | else {
255 | if s.is_number(c) {
256 | for s.is_number(s.th_next()) {
257 | s.next(true)
258 | }
259 | return token.create_token(.number, s.filename, line_nr, char_nr, s.lit)
260 | } else if s.is_name(c) {
261 | for s.is_name(s.th_next()) {
262 | _, e := s.next(true)
263 | if e {
264 | break
265 | }
266 | }
267 | if string(s.lit) in token.keywords {
268 | return token.create_token(token.keywords[string(s.lit)], s.filename, line_nr, char_nr, s.lit)
269 | }
270 | return token.create_token(.name, s.filename, line_nr, char_nr, s.lit)
271 | }
272 | }
273 | }
274 | return token.create_token(.unknown, s.filename, line_nr, char_nr, s.lit)
275 | }
276 |
277 | // gets the next byte in string and increases values
278 | fn (mut s Scanner) next(app bool, extra ...bool) (byte, bool) {
279 | mut b := byte(0)
280 | mut ba := false
281 | if s.pos < s.data.len {
282 | b = s.data[s.pos]
283 | if s.pos + 1 < s.data.len {
284 | ba = s.validate(s.data[s.pos+1])
285 | } else {
286 | ba = true
287 | }
288 | if app {
289 | s.lit << b
290 | } else {
291 | s.lit[0] = b
292 | }
293 | if extra.len > 0 && extra[0] {
294 | s.pos++
295 | s.char_nr++
296 | if b == `\n` {
297 | s.line_nr++
298 | s.char_nr = 0
299 | }
300 | } else {
301 | s.pos++
302 | s.char_nr++
303 | for {
304 | if s.pos >= s.data.len {
305 | break
306 | }
307 | if s.data[s.pos] == ` ` || s.data[s.pos] == `\t` {
308 | s.pos++
309 | s.char_nr++
310 | continue
311 | } else if s.data[s.pos] == `\n` {
312 | s.pos++
313 | s.line_nr++
314 | s.char_nr = 0
315 | continue
316 | } else {
317 | break
318 | }
319 | }
320 | }
321 | }
322 | return b, ba
323 | }
324 |
325 | // returns the theoretical next byte
326 | fn (mut s Scanner) th_next() byte {
327 | mut b := byte(0)
328 | if s.pos < s.data.len {
329 | b = s.data[s.pos]
330 | }
331 | return b
332 | }
333 |
334 | fn (mut s Scanner) validate(c byte) bool {
335 | return c == ` ` || c == `\t` || c == `\n` || c == `\r`
336 | }
337 |
338 | fn (mut s Scanner) is_number(b byte) bool {
339 | return (b >= `0` && b <= `9`)
340 | }
341 |
342 | fn (mut s Scanner) is_name(b byte) bool {
343 | return s.is_number(b) || b == `_` || (b >= `a` && b <= `z`) || (b >= `A` && b <= `Z`) || b == `.`
344 | }
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | GNU GENERAL PUBLIC LICENSE
2 | Version 3, 29 June 2007
3 |
4 | Copyright (C) 2007 Free Software Foundation, Inc.
5 | Everyone is permitted to copy and distribute verbatim copies
6 | of this license document, but changing it is not allowed.
7 |
8 | Preamble
9 |
10 | The GNU General Public License is a free, copyleft license for
11 | software and other kinds of works.
12 |
13 | The licenses for most software and other practical works are designed
14 | to take away your freedom to share and change the works. By contrast,
15 | the GNU General Public License is intended to guarantee your freedom to
16 | share and change all versions of a program--to make sure it remains free
17 | software for all its users. We, the Free Software Foundation, use the
18 | GNU General Public License for most of our software; it applies also to
19 | any other work released this way by its authors. You can apply it to
20 | your programs, too.
21 |
22 | When we speak of free software, we are referring to freedom, not
23 | price. Our General Public Licenses are designed to make sure that you
24 | have the freedom to distribute copies of free software (and charge for
25 | them if you wish), that you receive source code or can get it if you
26 | want it, that you can change the software or use pieces of it in new
27 | free programs, and that you know you can do these things.
28 |
29 | To protect your rights, we need to prevent others from denying you
30 | these rights or asking you to surrender the rights. Therefore, you have
31 | certain responsibilities if you distribute copies of the software, or if
32 | you modify it: responsibilities to respect the freedom of others.
33 |
34 | For example, if you distribute copies of such a program, whether
35 | gratis or for a fee, you must pass on to the recipients the same
36 | freedoms that you received. You must make sure that they, too, receive
37 | or can get the source code. And you must show them these terms so they
38 | know their rights.
39 |
40 | Developers that use the GNU GPL protect your rights with two steps:
41 | (1) assert copyright on the software, and (2) offer you this License
42 | giving you legal permission to copy, distribute and/or modify it.
43 |
44 | For the developers' and authors' protection, the GPL clearly explains
45 | that there is no warranty for this free software. For both users' and
46 | authors' sake, the GPL requires that modified versions be marked as
47 | changed, so that their problems will not be attributed erroneously to
48 | authors of previous versions.
49 |
50 | Some devices are designed to deny users access to install or run
51 | modified versions of the software inside them, although the manufacturer
52 | can do so. This is fundamentally incompatible with the aim of
53 | protecting users' freedom to change the software. The systematic
54 | pattern of such abuse occurs in the area of products for individuals to
55 | use, which is precisely where it is most unacceptable. Therefore, we
56 | have designed this version of the GPL to prohibit the practice for those
57 | products. If such problems arise substantially in other domains, we
58 | stand ready to extend this provision to those domains in future versions
59 | of the GPL, as needed to protect the freedom of users.
60 |
61 | Finally, every program is threatened constantly by software patents.
62 | States should not allow patents to restrict development and use of
63 | software on general-purpose computers, but in those that do, we wish to
64 | avoid the special danger that patents applied to a free program could
65 | make it effectively proprietary. To prevent this, the GPL assures that
66 | patents cannot be used to render the program non-free.
67 |
68 | The precise terms and conditions for copying, distribution and
69 | modification follow.
70 |
71 | TERMS AND CONDITIONS
72 |
73 | 0. Definitions.
74 |
75 | "This License" refers to version 3 of the GNU General Public License.
76 |
77 | "Copyright" also means copyright-like laws that apply to other kinds of
78 | works, such as semiconductor masks.
79 |
80 | "The Program" refers to any copyrightable work licensed under this
81 | License. Each licensee is addressed as "you". "Licensees" and
82 | "recipients" may be individuals or organizations.
83 |
84 | To "modify" a work means to copy from or adapt all or part of the work
85 | in a fashion requiring copyright permission, other than the making of an
86 | exact copy. The resulting work is called a "modified version" of the
87 | earlier work or a work "based on" the earlier work.
88 |
89 | A "covered work" means either the unmodified Program or a work based
90 | on the Program.
91 |
92 | To "propagate" a work means to do anything with it that, without
93 | permission, would make you directly or secondarily liable for
94 | infringement under applicable copyright law, except executing it on a
95 | computer or modifying a private copy. Propagation includes copying,
96 | distribution (with or without modification), making available to the
97 | public, and in some countries other activities as well.
98 |
99 | To "convey" a work means any kind of propagation that enables other
100 | parties to make or receive copies. Mere interaction with a user through
101 | a computer network, with no transfer of a copy, is not conveying.
102 |
103 | An interactive user interface displays "Appropriate Legal Notices"
104 | to the extent that it includes a convenient and prominently visible
105 | feature that (1) displays an appropriate copyright notice, and (2)
106 | tells the user that there is no warranty for the work (except to the
107 | extent that warranties are provided), that licensees may convey the
108 | work under this License, and how to view a copy of this License. If
109 | the interface presents a list of user commands or options, such as a
110 | menu, a prominent item in the list meets this criterion.
111 |
112 | 1. Source Code.
113 |
114 | The "source code" for a work means the preferred form of the work
115 | for making modifications to it. "Object code" means any non-source
116 | form of a work.
117 |
118 | A "Standard Interface" means an interface that either is an official
119 | standard defined by a recognized standards body, or, in the case of
120 | interfaces specified for a particular programming language, one that
121 | is widely used among developers working in that language.
122 |
123 | The "System Libraries" of an executable work include anything, other
124 | than the work as a whole, that (a) is included in the normal form of
125 | packaging a Major Component, but which is not part of that Major
126 | Component, and (b) serves only to enable use of the work with that
127 | Major Component, or to implement a Standard Interface for which an
128 | implementation is available to the public in source code form. A
129 | "Major Component", in this context, means a major essential component
130 | (kernel, window system, and so on) of the specific operating system
131 | (if any) on which the executable work runs, or a compiler used to
132 | produce the work, or an object code interpreter used to run it.
133 |
134 | The "Corresponding Source" for a work in object code form means all
135 | the source code needed to generate, install, and (for an executable
136 | work) run the object code and to modify the work, including scripts to
137 | control those activities. However, it does not include the work's
138 | System Libraries, or general-purpose tools or generally available free
139 | programs which are used unmodified in performing those activities but
140 | which are not part of the work. For example, Corresponding Source
141 | includes interface definition files associated with source files for
142 | the work, and the source code for shared libraries and dynamically
143 | linked subprograms that the work is specifically designed to require,
144 | such as by intimate data communication or control flow between those
145 | subprograms and other parts of the work.
146 |
147 | The Corresponding Source need not include anything that users
148 | can regenerate automatically from other parts of the Corresponding
149 | Source.
150 |
151 | The Corresponding Source for a work in source code form is that
152 | same work.
153 |
154 | 2. Basic Permissions.
155 |
156 | All rights granted under this License are granted for the term of
157 | copyright on the Program, and are irrevocable provided the stated
158 | conditions are met. This License explicitly affirms your unlimited
159 | permission to run the unmodified Program. The output from running a
160 | covered work is covered by this License only if the output, given its
161 | content, constitutes a covered work. This License acknowledges your
162 | rights of fair use or other equivalent, as provided by copyright law.
163 |
164 | You may make, run and propagate covered works that you do not
165 | convey, without conditions so long as your license otherwise remains
166 | in force. You may convey covered works to others for the sole purpose
167 | of having them make modifications exclusively for you, or provide you
168 | with facilities for running those works, provided that you comply with
169 | the terms of this License in conveying all material for which you do
170 | not control copyright. Those thus making or running the covered works
171 | for you must do so exclusively on your behalf, under your direction
172 | and control, on terms that prohibit them from making any copies of
173 | your copyrighted material outside their relationship with you.
174 |
175 | Conveying under any other circumstances is permitted solely under
176 | the conditions stated below. Sublicensing is not allowed; section 10
177 | makes it unnecessary.
178 |
179 | 3. Protecting Users' Legal Rights From Anti-Circumvention Law.
180 |
181 | No covered work shall be deemed part of an effective technological
182 | measure under any applicable law fulfilling obligations under article
183 | 11 of the WIPO copyright treaty adopted on 20 December 1996, or
184 | similar laws prohibiting or restricting circumvention of such
185 | measures.
186 |
187 | When you convey a covered work, you waive any legal power to forbid
188 | circumvention of technological measures to the extent such circumvention
189 | is effected by exercising rights under this License with respect to
190 | the covered work, and you disclaim any intention to limit operation or
191 | modification of the work as a means of enforcing, against the work's
192 | users, your or third parties' legal rights to forbid circumvention of
193 | technological measures.
194 |
195 | 4. Conveying Verbatim Copies.
196 |
197 | You may convey verbatim copies of the Program's source code as you
198 | receive it, in any medium, provided that you conspicuously and
199 | appropriately publish on each copy an appropriate copyright notice;
200 | keep intact all notices stating that this License and any
201 | non-permissive terms added in accord with section 7 apply to the code;
202 | keep intact all notices of the absence of any warranty; and give all
203 | recipients a copy of this License along with the Program.
204 |
205 | You may charge any price or no price for each copy that you convey,
206 | and you may offer support or warranty protection for a fee.
207 |
208 | 5. Conveying Modified Source Versions.
209 |
210 | You may convey a work based on the Program, or the modifications to
211 | produce it from the Program, in the form of source code under the
212 | terms of section 4, provided that you also meet all of these conditions:
213 |
214 | a) The work must carry prominent notices stating that you modified
215 | it, and giving a relevant date.
216 |
217 | b) The work must carry prominent notices stating that it is
218 | released under this License and any conditions added under section
219 | 7. This requirement modifies the requirement in section 4 to
220 | "keep intact all notices".
221 |
222 | c) You must license the entire work, as a whole, under this
223 | License to anyone who comes into possession of a copy. This
224 | License will therefore apply, along with any applicable section 7
225 | additional terms, to the whole of the work, and all its parts,
226 | regardless of how they are packaged. This License gives no
227 | permission to license the work in any other way, but it does not
228 | invalidate such permission if you have separately received it.
229 |
230 | d) If the work has interactive user interfaces, each must display
231 | Appropriate Legal Notices; however, if the Program has interactive
232 | interfaces that do not display Appropriate Legal Notices, your
233 | work need not make them do so.
234 |
235 | A compilation of a covered work with other separate and independent
236 | works, which are not by their nature extensions of the covered work,
237 | and which are not combined with it such as to form a larger program,
238 | in or on a volume of a storage or distribution medium, is called an
239 | "aggregate" if the compilation and its resulting copyright are not
240 | used to limit the access or legal rights of the compilation's users
241 | beyond what the individual works permit. Inclusion of a covered work
242 | in an aggregate does not cause this License to apply to the other
243 | parts of the aggregate.
244 |
245 | 6. Conveying Non-Source Forms.
246 |
247 | You may convey a covered work in object code form under the terms
248 | of sections 4 and 5, provided that you also convey the
249 | machine-readable Corresponding Source under the terms of this License,
250 | in one of these ways:
251 |
252 | a) Convey the object code in, or embodied in, a physical product
253 | (including a physical distribution medium), accompanied by the
254 | Corresponding Source fixed on a durable physical medium
255 | customarily used for software interchange.
256 |
257 | b) Convey the object code in, or embodied in, a physical product
258 | (including a physical distribution medium), accompanied by a
259 | written offer, valid for at least three years and valid for as
260 | long as you offer spare parts or customer support for that product
261 | model, to give anyone who possesses the object code either (1) a
262 | copy of the Corresponding Source for all the software in the
263 | product that is covered by this License, on a durable physical
264 | medium customarily used for software interchange, for a price no
265 | more than your reasonable cost of physically performing this
266 | conveying of source, or (2) access to copy the
267 | Corresponding Source from a network server at no charge.
268 |
269 | c) Convey individual copies of the object code with a copy of the
270 | written offer to provide the Corresponding Source. This
271 | alternative is allowed only occasionally and noncommercially, and
272 | only if you received the object code with such an offer, in accord
273 | with subsection 6b.
274 |
275 | d) Convey the object code by offering access from a designated
276 | place (gratis or for a charge), and offer equivalent access to the
277 | Corresponding Source in the same way through the same place at no
278 | further charge. You need not require recipients to copy the
279 | Corresponding Source along with the object code. If the place to
280 | copy the object code is a network server, the Corresponding Source
281 | may be on a different server (operated by you or a third party)
282 | that supports equivalent copying facilities, provided you maintain
283 | clear directions next to the object code saying where to find the
284 | Corresponding Source. Regardless of what server hosts the
285 | Corresponding Source, you remain obligated to ensure that it is
286 | available for as long as needed to satisfy these requirements.
287 |
288 | e) Convey the object code using peer-to-peer transmission, provided
289 | you inform other peers where the object code and Corresponding
290 | Source of the work are being offered to the general public at no
291 | charge under subsection 6d.
292 |
293 | A separable portion of the object code, whose source code is excluded
294 | from the Corresponding Source as a System Library, need not be
295 | included in conveying the object code work.
296 |
297 | A "User Product" is either (1) a "consumer product", which means any
298 | tangible personal property which is normally used for personal, family,
299 | or household purposes, or (2) anything designed or sold for incorporation
300 | into a dwelling. In determining whether a product is a consumer product,
301 | doubtful cases shall be resolved in favor of coverage. For a particular
302 | product received by a particular user, "normally used" refers to a
303 | typical or common use of that class of product, regardless of the status
304 | of the particular user or of the way in which the particular user
305 | actually uses, or expects or is expected to use, the product. A product
306 | is a consumer product regardless of whether the product has substantial
307 | commercial, industrial or non-consumer uses, unless such uses represent
308 | the only significant mode of use of the product.
309 |
310 | "Installation Information" for a User Product means any methods,
311 | procedures, authorization keys, or other information required to install
312 | and execute modified versions of a covered work in that User Product from
313 | a modified version of its Corresponding Source. The information must
314 | suffice to ensure that the continued functioning of the modified object
315 | code is in no case prevented or interfered with solely because
316 | modification has been made.
317 |
318 | If you convey an object code work under this section in, or with, or
319 | specifically for use in, a User Product, and the conveying occurs as
320 | part of a transaction in which the right of possession and use of the
321 | User Product is transferred to the recipient in perpetuity or for a
322 | fixed term (regardless of how the transaction is characterized), the
323 | Corresponding Source conveyed under this section must be accompanied
324 | by the Installation Information. But this requirement does not apply
325 | if neither you nor any third party retains the ability to install
326 | modified object code on the User Product (for example, the work has
327 | been installed in ROM).
328 |
329 | The requirement to provide Installation Information does not include a
330 | requirement to continue to provide support service, warranty, or updates
331 | for a work that has been modified or installed by the recipient, or for
332 | the User Product in which it has been modified or installed. Access to a
333 | network may be denied when the modification itself materially and
334 | adversely affects the operation of the network or violates the rules and
335 | protocols for communication across the network.
336 |
337 | Corresponding Source conveyed, and Installation Information provided,
338 | in accord with this section must be in a format that is publicly
339 | documented (and with an implementation available to the public in
340 | source code form), and must require no special password or key for
341 | unpacking, reading or copying.
342 |
343 | 7. Additional Terms.
344 |
345 | "Additional permissions" are terms that supplement the terms of this
346 | License by making exceptions from one or more of its conditions.
347 | Additional permissions that are applicable to the entire Program shall
348 | be treated as though they were included in this License, to the extent
349 | that they are valid under applicable law. If additional permissions
350 | apply only to part of the Program, that part may be used separately
351 | under those permissions, but the entire Program remains governed by
352 | this License without regard to the additional permissions.
353 |
354 | When you convey a copy of a covered work, you may at your option
355 | remove any additional permissions from that copy, or from any part of
356 | it. (Additional permissions may be written to require their own
357 | removal in certain cases when you modify the work.) You may place
358 | additional permissions on material, added by you to a covered work,
359 | for which you have or can give appropriate copyright permission.
360 |
361 | Notwithstanding any other provision of this License, for material you
362 | add to a covered work, you may (if authorized by the copyright holders of
363 | that material) supplement the terms of this License with terms:
364 |
365 | a) Disclaiming warranty or limiting liability differently from the
366 | terms of sections 15 and 16 of this License; or
367 |
368 | b) Requiring preservation of specified reasonable legal notices or
369 | author attributions in that material or in the Appropriate Legal
370 | Notices displayed by works containing it; or
371 |
372 | c) Prohibiting misrepresentation of the origin of that material, or
373 | requiring that modified versions of such material be marked in
374 | reasonable ways as different from the original version; or
375 |
376 | d) Limiting the use for publicity purposes of names of licensors or
377 | authors of the material; or
378 |
379 | e) Declining to grant rights under trademark law for use of some
380 | trade names, trademarks, or service marks; or
381 |
382 | f) Requiring indemnification of licensors and authors of that
383 | material by anyone who conveys the material (or modified versions of
384 | it) with contractual assumptions of liability to the recipient, for
385 | any liability that these contractual assumptions directly impose on
386 | those licensors and authors.
387 |
388 | All other non-permissive additional terms are considered "further
389 | restrictions" within the meaning of section 10. If the Program as you
390 | received it, or any part of it, contains a notice stating that it is
391 | governed by this License along with a term that is a further
392 | restriction, you may remove that term. If a license document contains
393 | a further restriction but permits relicensing or conveying under this
394 | License, you may add to a covered work material governed by the terms
395 | of that license document, provided that the further restriction does
396 | not survive such relicensing or conveying.
397 |
398 | If you add terms to a covered work in accord with this section, you
399 | must place, in the relevant source files, a statement of the
400 | additional terms that apply to those files, or a notice indicating
401 | where to find the applicable terms.
402 |
403 | Additional terms, permissive or non-permissive, may be stated in the
404 | form of a separately written license, or stated as exceptions;
405 | the above requirements apply either way.
406 |
407 | 8. Termination.
408 |
409 | You may not propagate or modify a covered work except as expressly
410 | provided under this License. Any attempt otherwise to propagate or
411 | modify it is void, and will automatically terminate your rights under
412 | this License (including any patent licenses granted under the third
413 | paragraph of section 11).
414 |
415 | However, if you cease all violation of this License, then your
416 | license from a particular copyright holder is reinstated (a)
417 | provisionally, unless and until the copyright holder explicitly and
418 | finally terminates your license, and (b) permanently, if the copyright
419 | holder fails to notify you of the violation by some reasonable means
420 | prior to 60 days after the cessation.
421 |
422 | Moreover, your license from a particular copyright holder is
423 | reinstated permanently if the copyright holder notifies you of the
424 | violation by some reasonable means, this is the first time you have
425 | received notice of violation of this License (for any work) from that
426 | copyright holder, and you cure the violation prior to 30 days after
427 | your receipt of the notice.
428 |
429 | Termination of your rights under this section does not terminate the
430 | licenses of parties who have received copies or rights from you under
431 | this License. If your rights have been terminated and not permanently
432 | reinstated, you do not qualify to receive new licenses for the same
433 | material under section 10.
434 |
435 | 9. Acceptance Not Required for Having Copies.
436 |
437 | You are not required to accept this License in order to receive or
438 | run a copy of the Program. Ancillary propagation of a covered work
439 | occurring solely as a consequence of using peer-to-peer transmission
440 | to receive a copy likewise does not require acceptance. However,
441 | nothing other than this License grants you permission to propagate or
442 | modify any covered work. These actions infringe copyright if you do
443 | not accept this License. Therefore, by modifying or propagating a
444 | covered work, you indicate your acceptance of this License to do so.
445 |
446 | 10. Automatic Licensing of Downstream Recipients.
447 |
448 | Each time you convey a covered work, the recipient automatically
449 | receives a license from the original licensors, to run, modify and
450 | propagate that work, subject to this License. You are not responsible
451 | for enforcing compliance by third parties with this License.
452 |
453 | An "entity transaction" is a transaction transferring control of an
454 | organization, or substantially all assets of one, or subdividing an
455 | organization, or merging organizations. If propagation of a covered
456 | work results from an entity transaction, each party to that
457 | transaction who receives a copy of the work also receives whatever
458 | licenses to the work the party's predecessor in interest had or could
459 | give under the previous paragraph, plus a right to possession of the
460 | Corresponding Source of the work from the predecessor in interest, if
461 | the predecessor has it or can get it with reasonable efforts.
462 |
463 | You may not impose any further restrictions on the exercise of the
464 | rights granted or affirmed under this License. For example, you may
465 | not impose a license fee, royalty, or other charge for exercise of
466 | rights granted under this License, and you may not initiate litigation
467 | (including a cross-claim or counterclaim in a lawsuit) alleging that
468 | any patent claim is infringed by making, using, selling, offering for
469 | sale, or importing the Program or any portion of it.
470 |
471 | 11. Patents.
472 |
473 | A "contributor" is a copyright holder who authorizes use under this
474 | License of the Program or a work on which the Program is based. The
475 | work thus licensed is called the contributor's "contributor version".
476 |
477 | A contributor's "essential patent claims" are all patent claims
478 | owned or controlled by the contributor, whether already acquired or
479 | hereafter acquired, that would be infringed by some manner, permitted
480 | by this License, of making, using, or selling its contributor version,
481 | but do not include claims that would be infringed only as a
482 | consequence of further modification of the contributor version. For
483 | purposes of this definition, "control" includes the right to grant
484 | patent sublicenses in a manner consistent with the requirements of
485 | this License.
486 |
487 | Each contributor grants you a non-exclusive, worldwide, royalty-free
488 | patent license under the contributor's essential patent claims, to
489 | make, use, sell, offer for sale, import and otherwise run, modify and
490 | propagate the contents of its contributor version.
491 |
492 | In the following three paragraphs, a "patent license" is any express
493 | agreement or commitment, however denominated, not to enforce a patent
494 | (such as an express permission to practice a patent or covenant not to
495 | sue for patent infringement). To "grant" such a patent license to a
496 | party means to make such an agreement or commitment not to enforce a
497 | patent against the party.
498 |
499 | If you convey a covered work, knowingly relying on a patent license,
500 | and the Corresponding Source of the work is not available for anyone
501 | to copy, free of charge and under the terms of this License, through a
502 | publicly available network server or other readily accessible means,
503 | then you must either (1) cause the Corresponding Source to be so
504 | available, or (2) arrange to deprive yourself of the benefit of the
505 | patent license for this particular work, or (3) arrange, in a manner
506 | consistent with the requirements of this License, to extend the patent
507 | license to downstream recipients. "Knowingly relying" means you have
508 | actual knowledge that, but for the patent license, your conveying the
509 | covered work in a country, or your recipient's use of the covered work
510 | in a country, would infringe one or more identifiable patents in that
511 | country that you have reason to believe are valid.
512 |
513 | If, pursuant to or in connection with a single transaction or
514 | arrangement, you convey, or propagate by procuring conveyance of, a
515 | covered work, and grant a patent license to some of the parties
516 | receiving the covered work authorizing them to use, propagate, modify
517 | or convey a specific copy of the covered work, then the patent license
518 | you grant is automatically extended to all recipients of the covered
519 | work and works based on it.
520 |
521 | A patent license is "discriminatory" if it does not include within
522 | the scope of its coverage, prohibits the exercise of, or is
523 | conditioned on the non-exercise of one or more of the rights that are
524 | specifically granted under this License. You may not convey a covered
525 | work if you are a party to an arrangement with a third party that is
526 | in the business of distributing software, under which you make payment
527 | to the third party based on the extent of your activity of conveying
528 | the work, and under which the third party grants, to any of the
529 | parties who would receive the covered work from you, a discriminatory
530 | patent license (a) in connection with copies of the covered work
531 | conveyed by you (or copies made from those copies), or (b) primarily
532 | for and in connection with specific products or compilations that
533 | contain the covered work, unless you entered into that arrangement,
534 | or that patent license was granted, prior to 28 March 2007.
535 |
536 | Nothing in this License shall be construed as excluding or limiting
537 | any implied license or other defenses to infringement that may
538 | otherwise be available to you under applicable patent law.
539 |
540 | 12. No Surrender of Others' Freedom.
541 |
542 | If conditions are imposed on you (whether by court order, agreement or
543 | otherwise) that contradict the conditions of this License, they do not
544 | excuse you from the conditions of this License. If you cannot convey a
545 | covered work so as to satisfy simultaneously your obligations under this
546 | License and any other pertinent obligations, then as a consequence you may
547 | not convey it at all. For example, if you agree to terms that obligate you
548 | to collect a royalty for further conveying from those to whom you convey
549 | the Program, the only way you could satisfy both those terms and this
550 | License would be to refrain entirely from conveying the Program.
551 |
552 | 13. Use with the GNU Affero General Public License.
553 |
554 | Notwithstanding any other provision of this License, you have
555 | permission to link or combine any covered work with a work licensed
556 | under version 3 of the GNU Affero General Public License into a single
557 | combined work, and to convey the resulting work. The terms of this
558 | License will continue to apply to the part which is the covered work,
559 | but the special requirements of the GNU Affero General Public License,
560 | section 13, concerning interaction through a network will apply to the
561 | combination as such.
562 |
563 | 14. Revised Versions of this License.
564 |
565 | The Free Software Foundation may publish revised and/or new versions of
566 | the GNU General Public License from time to time. Such new versions will
567 | be similar in spirit to the present version, but may differ in detail to
568 | address new problems or concerns.
569 |
570 | Each version is given a distinguishing version number. If the
571 | Program specifies that a certain numbered version of the GNU General
572 | Public License "or any later version" applies to it, you have the
573 | option of following the terms and conditions either of that numbered
574 | version or of any later version published by the Free Software
575 | Foundation. If the Program does not specify a version number of the
576 | GNU General Public License, you may choose any version ever published
577 | by the Free Software Foundation.
578 |
579 | If the Program specifies that a proxy can decide which future
580 | versions of the GNU General Public License can be used, that proxy's
581 | public statement of acceptance of a version permanently authorizes you
582 | to choose that version for the Program.
583 |
584 | Later license versions may give you additional or different
585 | permissions. However, no additional obligations are imposed on any
586 | author or copyright holder as a result of your choosing to follow a
587 | later version.
588 |
589 | 15. Disclaimer of Warranty.
590 |
591 | THERE IS NO WARRANTY FOR THE PROGRAM, TO THE EXTENT PERMITTED BY
592 | APPLICABLE LAW. EXCEPT WHEN OTHERWISE STATED IN WRITING THE COPYRIGHT
593 | HOLDERS AND/OR OTHER PARTIES PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY
594 | OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO,
595 | THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
596 | PURPOSE. THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE PROGRAM
597 | IS WITH YOU. SHOULD THE PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF
598 | ALL NECESSARY SERVICING, REPAIR OR CORRECTION.
599 |
600 | 16. Limitation of Liability.
601 |
602 | IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
603 | WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MODIFIES AND/OR CONVEYS
604 | THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, INCLUDING ANY
605 | GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING OUT OF THE
606 | USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED TO LOSS OF
607 | DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY YOU OR THIRD
608 | PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER PROGRAMS),
609 | EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE POSSIBILITY OF
610 | SUCH DAMAGES.
611 |
612 | 17. Interpretation of Sections 15 and 16.
613 |
614 | If the disclaimer of warranty and limitation of liability provided
615 | above cannot be given local legal effect according to their terms,
616 | reviewing courts shall apply local law that most closely approximates
617 | an absolute waiver of all civil liability in connection with the
618 | Program, unless a warranty or assumption of liability accompanies a
619 | copy of the Program in return for a fee.
620 |
621 | END OF TERMS AND CONDITIONS
622 |
623 | How to Apply These Terms to Your New Programs
624 |
625 | If you develop a new program, and you want it to be of the greatest
626 | possible use to the public, the best way to achieve this is to make it
627 | free software which everyone can redistribute and change under these terms.
628 |
629 | To do so, attach the following notices to the program. It is safest
630 | to attach them to the start of each source file to most effectively
631 | state the exclusion of warranty; and each file should have at least
632 | the "copyright" line and a pointer to where the full notice is found.
633 |
634 |
635 | Copyright (C)
636 |
637 | This program is free software: you can redistribute it and/or modify
638 | it under the terms of the GNU General Public License as published by
639 | the Free Software Foundation, either version 3 of the License, or
640 | (at your option) any later version.
641 |
642 | This program is distributed in the hope that it will be useful,
643 | but WITHOUT ANY WARRANTY; without even the implied warranty of
644 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
645 | GNU General Public License for more details.
646 |
647 | You should have received a copy of the GNU General Public License
648 | along with this program. If not, see .
649 |
650 | Also add information on how to contact you by electronic and paper mail.
651 |
652 | If the program does terminal interaction, make it output a short
653 | notice like this when it starts in an interactive mode:
654 |
655 | Copyright (C)
656 | This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
657 | This is free software, and you are welcome to redistribute it
658 | under certain conditions; type `show c' for details.
659 |
660 | The hypothetical commands `show w' and `show c' should show the appropriate
661 | parts of the General Public License. Of course, your program's commands
662 | might be different; for a GUI interface, you would use an "about box".
663 |
664 | You should also get your employer (if you work as a programmer) or school,
665 | if any, to sign a "copyright disclaimer" for the program, if necessary.
666 | For more information on this, and how to apply and follow the GNU GPL, see
667 | .
668 |
669 | The GNU General Public License does not permit incorporating your program
670 | into proprietary programs. If your program is a subroutine library, you
671 | may consider it more useful to permit linking proprietary applications with
672 | the library. If this is what you want to do, use the GNU Lesser General
673 | Public License instead of this License. But first, please read
674 | .
675 |
--------------------------------------------------------------------------------