├── .gitignore ├── .vscode ├── launch.json ├── settings.json ├── snippets.code-snippets └── tasks.json ├── LICENSE ├── README.md ├── cmd └── v │ ├── text.dot │ └── v.v ├── lib ├── comp │ ├── args.v │ ├── ast │ │ ├── ast_test.v │ │ ├── expr_array_init.v │ │ ├── expr_assign.v │ │ ├── expr_call.v │ │ ├── expr_empty.v │ │ ├── expr_if.v │ │ ├── expr_index.v │ │ ├── expr_literal.v │ │ ├── expr_name.v │ │ ├── expr_operator.v │ │ ├── expr_range.v │ │ ├── expr_struct_init.v │ │ ├── kind.v │ │ ├── node_comp.v │ │ ├── node_empty.v │ │ ├── node_fn_decl.v │ │ ├── node_param.v │ │ ├── node_struct_decl.v │ │ ├── node_type.v │ │ ├── operator_prec.v │ │ ├── receiver_node.v │ │ ├── sep_syntax_list.v │ │ ├── stmt_assert.v │ │ ├── stmt_block.v │ │ ├── stmt_break.v │ │ ├── stmt_comment.v │ │ ├── stmt_continue.v │ │ ├── stmt_expr.v │ │ ├── stmt_for.v │ │ ├── stmt_global.v │ │ ├── stmt_if.v │ │ ├── stmt_import.v │ │ ├── stmt_module.v │ │ ├── stmt_return.v │ │ ├── stmt_var_decl.v │ │ ├── syntax_tree.v │ │ ├── type.v │ │ └── walker │ │ │ ├── print.v │ │ │ └── walker.v │ ├── binding │ │ ├── binder.v │ │ ├── control_flow_graph.v │ │ ├── convertion │ │ │ └── conversion.v │ │ ├── expr_array_init.v │ │ ├── expr_assign.v │ │ ├── expr_binary.v │ │ ├── expr_call.v │ │ ├── expr_conv.v │ │ ├── expr_empty.v │ │ ├── expr_error.v │ │ ├── expr_if.v │ │ ├── expr_index.v │ │ ├── expr_literal.v │ │ ├── expr_none.v │ │ ├── expr_range.v │ │ ├── expr_struct_init.v │ │ ├── expr_unary.v │ │ ├── expr_var.v │ │ ├── gl_scope_stack.v │ │ ├── gl_scope_stack_test.v │ │ ├── lowerer_eval.v │ │ ├── lowerer_factory.v │ │ ├── modules.md │ │ ├── node_printer.v │ │ ├── operators.v │ │ ├── program.v │ │ ├── scope.v │ │ ├── scope_test.v │ │ ├── stack_bound_stmt.v │ │ ├── stack_brk_cont_lbls.v │ │ ├── stmt_assert.v │ │ ├── stmt_block.v │ │ ├── stmt_break.v │ │ ├── stmt_comment.v │ │ ├── stmt_cond_goto.v │ │ ├── stmt_continue.v │ │ ├── stmt_expr.v │ │ ├── stmt_for.v │ │ ├── stmt_goto.v │ │ ├── stmt_if.v │ │ ├── stmt_import.v │ │ ├── stmt_label.v │ │ ├── stmt_module.v │ │ ├── stmt_return.v │ │ ├── stmt_var_decl.v │ │ ├── type.v │ │ ├── variables.v │ │ └── walker │ │ │ ├── print.v │ │ │ └── walker.v │ ├── comp.v │ ├── eval.v │ ├── gen │ │ ├── gen.v │ │ ├── golang │ │ │ ├── gen_fns.v │ │ │ ├── gen_nodes.v │ │ │ ├── go_gen.v │ │ │ ├── lowerer_go.v │ │ │ └── templates │ │ │ │ └── main.go │ │ └── llvm │ │ │ ├── core │ │ │ ├── bindings.v │ │ │ └── llvm.c.v │ │ │ ├── emit │ │ │ ├── emit_fn_call.v │ │ │ ├── emit_fn_decl.v │ │ │ ├── emit_fn_stmts.v │ │ │ ├── emit_globals.v │ │ │ ├── emit_module.v │ │ │ ├── emit_tests.v │ │ │ └── emit_types.v │ │ │ ├── llvm_gen.v │ │ │ └── test.c │ ├── grammar.md │ ├── io │ │ ├── code_writer.v │ │ ├── node_string_writer.v │ │ ├── string_writer.v │ │ └── text_writer.v │ ├── parser │ │ ├── eval_test.v │ │ ├── parser.v │ │ ├── parser_test.v │ │ ├── pattern_match.v │ │ └── precedence_test.v │ ├── readme.md │ ├── symbols │ │ ├── built_in.v │ │ ├── symb_const.v │ │ ├── symb_function.v │ │ ├── symb_param.v │ │ ├── symb_type_any.v │ │ ├── symb_type_array.v │ │ ├── symb_type_built_in.v │ │ ├── symb_type_error.v │ │ ├── symb_type_struct.v │ │ ├── symb_type_void.v │ │ ├── symb_variable.v │ │ ├── symbol_printer.v │ │ └── types.v │ ├── token │ │ ├── token.v │ │ ├── tokenizer.v │ │ └── tokenizer_test.v │ └── util │ │ ├── annotated_text.v │ │ ├── annotated_text_test.v │ │ ├── mod │ │ ├── mod_lookup.v │ │ ├── mod_lookup_test.v │ │ └── tests │ │ │ ├── nomod │ │ │ └── a │ │ │ │ └── a.v │ │ │ └── vmod │ │ │ ├── lib │ │ │ ├── a │ │ │ │ └── a.v │ │ │ └── b │ │ │ │ └── b.v │ │ │ └── v.mod │ │ ├── pref │ │ └── pref.v │ │ ├── source │ │ ├── diag_diagnostics.v │ │ ├── diag_err_msg.v │ │ ├── diag_print_diag.v │ │ ├── diag_text_location.v │ │ ├── io.v │ │ ├── pos.v │ │ ├── source_code_test.v │ │ └── source_text.v │ │ ├── stack.v │ │ └── stack_test.v ├── repl │ ├── buffer.v │ ├── io.v │ ├── repl.v │ └── ui.v └── runtime │ ├── standard.c.v │ └── string.v ├── prof.txt ├── samples ├── .gitignore ├── arrays │ └── arrays.v ├── c_interop │ └── time.v ├── hello │ ├── hello_world.s │ └── hello_world.v ├── loops │ └── for_loop.v ├── main_fn │ ├── main_fn.s │ └── main_fn.v ├── module │ ├── module.v │ └── my_mod │ │ └── my_mod.v ├── mult_files │ ├── greet.v │ └── main.v ├── references │ └── main.v ├── strings │ └── strings.v ├── struct_fn │ └── struct_fn.v └── structs │ └── structs.v ├── tests ├── c_interop │ └── test_time.v ├── operators │ ├── convertion_test.v │ └── operator_test.v ├── strings │ └── strings_test.v ├── structs │ └── struct_test.v └── variables │ ├── arrays_test.v │ ├── mutable_test.v │ └── variables_test.v └── v.mod /.gitignore: -------------------------------------------------------------------------------- 1 | * 2 | !*/ 3 | !*.* 4 | !makefile 5 | *.o 6 | *.exe 7 | *.ll 8 | *.s 9 | *.bc 10 | *.out 11 | *.c -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | { 8 | "name": "(gdb) Launch", 9 | "type": "cppdbg", 10 | "request": "launch", 11 | "program": "${workspaceFolder}/cmd/v/v", 12 | "args": [ 13 | "test-self" 14 | // "../../samples/llvm/llvm.v" 15 | ], 16 | "stopAtEntry": true, 17 | "cwd": "${workspaceFolder}/cmd/v", 18 | "environment": [], 19 | "console": "externalTerminal", 20 | "MIMode": "gdb", 21 | "setupCommands": [ 22 | { 23 | "description": "Enable pretty-printing for gdb", 24 | "text": "-enable-pretty-printing", 25 | "ignoreFailures": true 26 | } 27 | ] 28 | } 29 | // , 30 | // { 31 | // "type": "by-gdb", 32 | // "request": "launch", 33 | // "name": "Launch(gdb)", 34 | // "program": "v", 35 | // "cwd": "${workspaceRoot}/cmd/v" 36 | // } 37 | ] 38 | } -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "python.formatting.provider": "black" 3 | } -------------------------------------------------------------------------------- /.vscode/snippets.code-snippets: -------------------------------------------------------------------------------- 1 | { 2 | "Ast-Node-Stmt": { 3 | "prefix": "ast-node-stmt", 4 | "body": [ 5 | "module ast", 6 | "", 7 | "import lib.comp.token", 8 | "import lib.comp.util.source", 9 | "", 10 | "pub struct $1Stmt {", 11 | "pub:", 12 | " // general ast node", 13 | " kind SyntaxKind = .$2_stmt", 14 | " pos source.Pos", 15 | " child_nodes []AstNode", 16 | " // child nodes", 17 | "}", 18 | "", 19 | "pub fn new_$2_stmt() $1Stmt {", 20 | " return $1Stmt{", 21 | " pos: source.new_pos_from_pos_bounds(key_for.pos, body_stmt.pos)", 22 | " child_nodes: [AstNode()]", 23 | " // insert nodes here", 24 | " }", 25 | "}", 26 | "pub fn (ex &$1Stmt) node_str() string {", 27 | " return typeof(ex).name", 28 | "}", 29 | ], 30 | "description": "" 31 | }, 32 | "Ast-Node-Expr": { 33 | "prefix": "ast-node-expr", 34 | "body": [ 35 | "module ast", 36 | "", 37 | "import lib.comp.token", 38 | "import lib.comp.util.source", 39 | "", 40 | "pub struct $1Expr {", 41 | "pub:", 42 | " // general ast node", 43 | " kind SyntaxKind = .$2_expr", 44 | " pos source.Pos", 45 | " child_nodes []AstNode", 46 | " // child nodes", 47 | "}", 48 | "", 49 | "pub fn new_$2_stmt() $1Expr {", 50 | " return $1Expr{", 51 | " pos: source.new_pos_from_pos_bounds(key_for.pos, body_stmt.pos", 52 | " child_nodes: [AstNode()]", 53 | " // insert nodes here", 54 | " }", 55 | "}", 56 | "pub fn (ex &$1Expr) node_str() string {", 57 | " return typeof(ex).name", 58 | "}", 59 | ], 60 | "description": "" 61 | } 62 | } -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | { 2 | // See https://go.microsoft.com/fwlink/?LinkId=733558 3 | // for the documentation about the tasks.json format 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "label": "format", 8 | "type": "shell", 9 | "command": "v fmt -w ${file}", 10 | "presentation": { 11 | "reveal": "silent" 12 | }, 13 | "problemMatcher": "$go" 14 | }, 15 | { 16 | "label": "mv", 17 | "type": "process", 18 | "command": "cmd/v/v", 19 | "args": [ 20 | "run", 21 | "${relativeFileDirname}" 22 | ], 23 | "presentation": { 24 | "reveal": "silent" 25 | }, 26 | "problemMatcher": "$go" 27 | }, 28 | { 29 | "label": "build", 30 | "type": "shell", 31 | "command": "v", 32 | "args": [ 33 | "-g", 34 | "-keepc", 35 | "cmd/v/v.v" 36 | ], 37 | "presentation": { 38 | "reveal": "silent" 39 | }, 40 | "problemMatcher": "$go" 41 | }, 42 | { 43 | "label": "test", 44 | "type": "shell", 45 | "command": "v", 46 | "args": [ 47 | "-g", 48 | "-keepc", 49 | "test-self", 50 | ], 51 | "presentation": { 52 | "reveal": "silent" 53 | }, 54 | "problemMatcher": "$go" 55 | } 56 | ] 57 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 Tomas Hellström 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # The micro V - minimal v language compiler 2 | This is a learning project to build a compiler for the awesome V language. Check out the [V website](https://vlang.io) and try it for yourself. 3 | 4 | 5 | # Overall design 6 | The compiler works like this: 7 | ## 1. Tokenizer 8 | The source text is tokenized into tokens that are parsed by the parser. 9 | 10 | ## 2. Parser 11 | The parser parses all tokens into an AST (abstract syntax tree). 12 | 13 | ## 3. Binding 14 | Analyses the the AST, binds the symbols and types, does the semantic checks etc. 15 | 16 | A part of the binding process is different levels of "lowering", take the AST node and make lower version. As an example: a for loop is turned inot labels and gotos. Depending on back-end different levels of lowering is needed. 17 | 18 | The binding also manage the scope and levels of scope. The GlobalScope is a special scope that has information of all top-level statements, constants and function declarations. 19 | 20 | ## 4. Compiler 21 | Handles the compiling process that starts with tokenizing and ends ether with evaluation/intepretation or a code generation to selected back-end 22 | 23 | ## 5. Evaluation 24 | Inteprets the lowered bound AST. 25 | 26 | ## 6. Code generation 27 | If not in intepreter scriptmode it will generate code depending on selected back-end. (Not decided yet the default one. Considering LLVM, C or MIL) 28 | 29 | # Modules 30 | ## lib module 31 | Lib module contiains all built-in libraries in micro V. 32 | 33 | ## lib.comp 34 | this module contains all logic that relates to the compiler/intepreter. 35 | 36 | ## lib.com.ast 37 | All the nodes in the abstract syntax tree. 38 | 39 | # Attribution 40 | Alexander Medvednikov, the creator of V and all V-developer for inpiring to this prototype 41 | -------------------------------------------------------------------------------- /cmd/v/text.dot: -------------------------------------------------------------------------------- 1 | digraph G { 2 | N1 [label = "" shape = box] 3 | N3 [label = "i := 10\lres := 0\l" shape = box] 4 | N4 [label = "Label_2:\lgoto Label_4 unless i == 0\l" shape = box] 5 | N5 [label = "goto Break_3\l" shape = box] 6 | N7 [label = "Label_4:\lres = res + 1\li = i - 1\l" shape = box] 7 | N8 [label = "Continue_1:\lgoto Label_2\l" shape = box] 8 | N10 [label = "Break_3:\lres\l" shape = box] 9 | N2 [label = "" shape = box] 10 | N1 -> N3 [label = ""] 11 | N3 -> N4 [label = ""] 12 | N4 -> N7 [label = "!i == 0"] 13 | N4 -> N5 [label = "i == 0"] 14 | N5 -> N10 [label = ""] 15 | N7 -> N8 [label = ""] 16 | N8 -> N4 [label = ""] 17 | N10 -> N2 [label = ""] 18 | } 19 | -------------------------------------------------------------------------------- /lib/comp/args.v: -------------------------------------------------------------------------------- 1 | module comp 2 | 3 | pub struct Args { 4 | } 5 | -------------------------------------------------------------------------------- /lib/comp/ast/ast_test.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source as src 5 | 6 | fn test_number_syntax_kind() { 7 | assert new_literal_expr(&SyntaxTree(0), token.Token{ 8 | kind: .number 9 | source: &src.SourceText(0) 10 | }, 0).kind == .literal_expr 11 | } 12 | 13 | fn test_binary_syntax_kind() { 14 | assert new_binary_expr(&SyntaxTree(0), new_literal_expr(&SyntaxTree(0), token.Token{ 15 | kind: .number 16 | source: &src.SourceText(0) 17 | }, 0), token.Token{ 18 | kind: .plus 19 | source: &src.SourceText(0) 20 | }, new_literal_expr(&SyntaxTree(0), token.Token{ kind: .number, source: &src.SourceText(0) }, 21 | 0)).kind == .binary_expr 22 | } 23 | -------------------------------------------------------------------------------- /lib/comp/ast/expr_array_init.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import strings 4 | import lib.comp.token 5 | import lib.comp.util.source 6 | 7 | pub struct ArrayInitExpr { 8 | pub: 9 | // general ast node 10 | tree &SyntaxTree 11 | kind SyntaxKind = .array_init_expr 12 | pos source.Pos 13 | child_nodes []AstNode 14 | // child nodes 15 | lsbr_tok token.Token 16 | rsbr_tok token.Token 17 | exprs []Expr // Ether values or the size expr `[expr, expr]` or `[expr]Type{}` 18 | len_expr Expr // the lenght expression 19 | cap_expr Expr // the cap expression 20 | init_expr Expr // the default value initializer expression 21 | is_val_array bool // if the array is initialized with values 22 | is_fixed bool // if fixed data is allocated on stack else data is allocated on heap 23 | } 24 | 25 | pub fn new_value_array_init_expr(tree &SyntaxTree, lsbr_tok token.Token, exprs []Expr, rsbr_tok token.Token, is_fixed bool) ArrayInitExpr { 26 | mut child_nodes := [AstNode(lsbr_tok)] 27 | child_nodes << exprs.map(AstNode(it)) 28 | child_nodes << rsbr_tok 29 | return ArrayInitExpr{ 30 | tree: tree 31 | pos: source.new_pos_from_pos_bounds(lsbr_tok.pos, rsbr_tok.pos) 32 | child_nodes: child_nodes 33 | // [expr, expr] 34 | lsbr_tok: lsbr_tok 35 | exprs: exprs 36 | rsbr_tok: lsbr_tok 37 | is_val_array: true 38 | is_fixed: is_fixed 39 | } 40 | } 41 | 42 | pub fn (ex &ArrayInitExpr) child_nodes() []AstNode { 43 | return ex.child_nodes 44 | } 45 | 46 | pub fn (ex ArrayInitExpr) text_location() source.TextLocation { 47 | return source.new_text_location(ex.tree.source, ex.pos) 48 | } 49 | 50 | pub fn (ex ArrayInitExpr) node_str() string { 51 | return typeof(ex).name 52 | } 53 | 54 | pub fn (ex ArrayInitExpr) str() string { 55 | if ex.is_val_array && ex.is_fixed { 56 | mut b := strings.new_builder(0) 57 | b.write_string('[') 58 | for expr in ex.exprs { 59 | b.write_string(expr.str()) 60 | } 61 | b.write_string(']!') 62 | return b.str() 63 | } 64 | 65 | return 'array unsupported' 66 | } 67 | -------------------------------------------------------------------------------- /lib/comp/ast/expr_assign.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | pub struct AssignExpr { 7 | pub: 8 | // general ast node 9 | tree &SyntaxTree 10 | kind SyntaxKind = .assign_expr 11 | pos source.Pos 12 | child_nodes []AstNode 13 | // child nodes 14 | name_expr NameExpr 15 | eq_tok token.Token 16 | expr Expr 17 | } 18 | 19 | pub fn new_assign_expr(tree &SyntaxTree, name_expr NameExpr, eq_tok token.Token, expr Expr) AssignExpr { 20 | return AssignExpr{ 21 | tree: tree 22 | name_expr: name_expr 23 | expr: expr 24 | eq_tok: eq_tok 25 | pos: source.new_pos_from_pos_bounds(name_expr.pos, expr.pos) 26 | child_nodes: [AstNode(Expr(name_expr)), eq_tok, expr] 27 | } 28 | } 29 | 30 | pub fn (ae &AssignExpr) child_nodes() []AstNode { 31 | return ae.child_nodes 32 | } 33 | 34 | pub fn (ex AssignExpr) text_location() source.TextLocation { 35 | return source.new_text_location(ex.tree.source, ex.pos) 36 | } 37 | 38 | pub fn (ex AssignExpr) node_str() string { 39 | return typeof(ex).name 40 | } 41 | 42 | pub fn (ex AssignExpr) str() string { 43 | return '${ex.name_expr.name_tok.lit} = $ex.expr' 44 | } 45 | -------------------------------------------------------------------------------- /lib/comp/ast/expr_call.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | pub struct CallExpr { 7 | pub: 8 | // tok token.Token 9 | // general ast node 10 | tree &SyntaxTree 11 | kind SyntaxKind = .call_expr 12 | pos source.Pos 13 | child_nodes []AstNode 14 | // child nodes 15 | lpar_tok token.Token 16 | name_expr NameExpr 17 | params []CallArgNode 18 | rpar_tok token.Token 19 | } 20 | 21 | pub fn new_call_expr(tree &SyntaxTree, name_expr NameExpr, lpar_tok token.Token, params []CallArgNode, rpar_tok token.Token) CallExpr { 22 | mut child_nodes := [AstNode(Expr(name_expr)), lpar_tok] 23 | for param in params { 24 | child_nodes << param 25 | } 26 | child_nodes << rpar_tok 27 | 28 | return CallExpr{ 29 | tree: tree 30 | pos: source.new_pos_from_pos_bounds(name_expr.name_tok.pos, rpar_tok.pos) 31 | child_nodes: child_nodes 32 | name_expr: name_expr 33 | lpar_tok: lpar_tok 34 | params: params 35 | rpar_tok: rpar_tok 36 | } 37 | } 38 | 39 | pub fn (le &CallExpr) child_nodes() []AstNode { 40 | return le.child_nodes 41 | } 42 | 43 | pub fn (ex CallExpr) text_location() source.TextLocation { 44 | return source.new_text_location(ex.tree.source, ex.pos) 45 | } 46 | 47 | pub fn (ex CallExpr) node_str() string { 48 | return typeof(ex).name 49 | } 50 | 51 | pub fn (ex CallExpr) str() string { 52 | mut ret := '${ex.name_expr.name_tok.lit}(' 53 | for i, param in ex.params { 54 | if i != 0 { 55 | ret = ret + ', ' 56 | } 57 | ret = ret + '$param' 58 | } 59 | ret = ret + ')' 60 | return ret 61 | } 62 | 63 | pub struct CallArgNode { 64 | pub: 65 | tree &SyntaxTree 66 | kind SyntaxKind = .call_arg_node 67 | pos source.Pos 68 | child_nodes []AstNode 69 | 70 | mut_tok token.Token 71 | expr Expr 72 | is_mut bool 73 | } 74 | 75 | pub fn new_call_arg_node(tree &SyntaxTree, mut_tok token.Token, expr Expr) CallArgNode { 76 | is_mut := mut_tok.kind == .key_mut 77 | 78 | child_nodes := if is_mut { [AstNode(mut_tok), expr] } else { [AstNode(expr)] } 79 | pos := if is_mut { source.new_pos_from_pos_bounds(mut_tok.pos, expr.pos) } else { expr.pos } 80 | return CallArgNode{ 81 | tree: tree 82 | pos: pos 83 | child_nodes: child_nodes 84 | mut_tok: mut_tok 85 | expr: expr 86 | is_mut: is_mut 87 | } 88 | } 89 | 90 | pub fn (le &CallArgNode) child_nodes() []AstNode { 91 | return le.child_nodes 92 | } 93 | 94 | pub fn (ex CallArgNode) text_location() source.TextLocation { 95 | return source.new_text_location(ex.tree.source, ex.pos) 96 | } 97 | 98 | pub fn (ex CallArgNode) node_str() string { 99 | return typeof(ex).name 100 | } 101 | 102 | pub fn (ex CallArgNode) str() string { 103 | if ex.is_mut { 104 | return 'mut $ex.expr' 105 | } 106 | return '$ex.expr' 107 | } 108 | -------------------------------------------------------------------------------- /lib/comp/ast/expr_empty.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | 5 | pub struct EmptyExpr { 6 | pub: 7 | // general ast node 8 | tree &SyntaxTree = &SyntaxTree(0) 9 | kind SyntaxKind = .empty_expr 10 | pos source.Pos 11 | child_nodes []AstNode 12 | } 13 | 14 | pub fn new_empty_expr() EmptyExpr { 15 | return EmptyExpr{} 16 | } 17 | 18 | pub fn (ex &EmptyExpr) child_nodes() []AstNode { 19 | return ex.child_nodes 20 | } 21 | 22 | pub fn (ex EmptyExpr) text_location() source.TextLocation { 23 | return source.new_text_location(ex.tree.source, ex.pos) 24 | } 25 | 26 | pub fn (ex EmptyExpr) node_str() string { 27 | return typeof(ex).name 28 | } 29 | 30 | pub fn (ex EmptyExpr) str() string { 31 | return '' 32 | } 33 | -------------------------------------------------------------------------------- /lib/comp/ast/expr_if.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | // Support if expression syntax 7 | // x := if i < 100 {10} else {20} 8 | pub struct IfExpr { 9 | pub: 10 | // general ast node 11 | tree &SyntaxTree 12 | kind SyntaxKind = .if_expr 13 | pos source.Pos 14 | child_nodes []AstNode 15 | // child nodes 16 | key_if token.Token 17 | key_else token.Token 18 | cond_expr Expr 19 | then_stmt Stmt 20 | else_stmt Stmt 21 | } 22 | 23 | pub fn new_if_expr(tree &SyntaxTree, key_if token.Token, cond_expr Expr, then_stmt Stmt, key_else token.Token, else_stmt Stmt) IfExpr { 24 | return IfExpr{ 25 | tree: tree 26 | key_if: key_if 27 | key_else: key_else 28 | cond_expr: cond_expr 29 | then_stmt: then_stmt 30 | else_stmt: else_stmt 31 | pos: source.new_pos_from_pos_bounds(key_if.pos, else_stmt.pos) 32 | child_nodes: [AstNode(key_if), cond_expr, then_stmt, key_else, else_stmt] 33 | } 34 | } 35 | 36 | pub fn (iss &IfExpr) child_nodes() []AstNode { 37 | return iss.child_nodes 38 | } 39 | 40 | pub fn (ex IfExpr) text_location() source.TextLocation { 41 | return source.new_text_location(ex.tree.source, ex.pos) 42 | } 43 | 44 | pub fn (ex IfExpr) node_str() string { 45 | return typeof(ex).name 46 | } 47 | 48 | pub fn (ex IfExpr) str() string { 49 | return 'if $ex.cond_expr {}' 50 | } 51 | -------------------------------------------------------------------------------- /lib/comp/ast/expr_index.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | pub struct IndexExpr { 7 | pub: 8 | // general ast node 9 | tree &SyntaxTree = &SyntaxTree(0) 10 | kind SyntaxKind = .index_expr 11 | pos source.Pos 12 | child_nodes []AstNode 13 | 14 | left_expr Expr 15 | 16 | lsbr token.Token 17 | index_expr Expr 18 | rsbr token.Token 19 | } 20 | 21 | pub fn new_index_expr(tree &SyntaxTree, left_expr Expr, lsbr token.Token, index_expr Expr, rsbr token.Token) IndexExpr { 22 | return IndexExpr{ 23 | tree: tree 24 | left_expr: left_expr 25 | lsbr: lsbr 26 | index_expr: index_expr 27 | rsbr: rsbr 28 | pos: source.new_pos_from_pos_bounds(lsbr.pos, rsbr.pos) 29 | child_nodes: [AstNode(left_expr), lsbr, index_expr, rsbr] 30 | } 31 | } 32 | 33 | pub fn (ex &IndexExpr) child_nodes() []AstNode { 34 | return ex.child_nodes 35 | } 36 | 37 | pub fn (ex IndexExpr) text_location() source.TextLocation { 38 | return source.new_text_location(ex.tree.source, ex.pos) 39 | } 40 | 41 | pub fn (ex IndexExpr) node_str() string { 42 | return typeof(ex).name 43 | } 44 | 45 | pub fn (ex IndexExpr) str() string { 46 | return '${ex.left_expr}[$ex.index_expr]' 47 | } -------------------------------------------------------------------------------- /lib/comp/ast/expr_literal.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.symbols 5 | import lib.comp.util.source 6 | 7 | pub struct LiteralExpr { 8 | tok token.Token 9 | pub: 10 | // general ast node 11 | tree &SyntaxTree 12 | kind SyntaxKind = .literal_expr 13 | pos source.Pos 14 | child_nodes []AstNode 15 | // child nodes 16 | val symbols.LitVal 17 | } 18 | 19 | pub fn new_literal_expr(tree &SyntaxTree, tok token.Token, val symbols.LitVal) LiteralExpr { 20 | if tok.kind !in [.number, .string, .key_true, .key_false] { 21 | panic('Expected a number token') 22 | } 23 | return LiteralExpr{ 24 | tree: tree 25 | tok: tok 26 | val: val 27 | pos: tok.pos 28 | child_nodes: [AstNode(tok)] 29 | } 30 | } 31 | 32 | pub fn (le &LiteralExpr) child_nodes() []AstNode { 33 | return le.child_nodes 34 | } 35 | 36 | pub fn (ex LiteralExpr) text_location() source.TextLocation { 37 | return source.new_text_location(ex.tree.source, ex.pos) 38 | } 39 | 40 | pub fn (ex LiteralExpr) node_str() string { 41 | return typeof(ex).name 42 | } 43 | 44 | pub fn (ex LiteralExpr) str() string { 45 | return '$ex.val' 46 | } 47 | -------------------------------------------------------------------------------- /lib/comp/ast/expr_name.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import strings 4 | import lib.comp.token 5 | import lib.comp.util.source 6 | 7 | pub struct NameExpr { 8 | pub: 9 | is_c_name bool 10 | is_ref bool 11 | // general ast node 12 | tree &SyntaxTree 13 | kind SyntaxKind = .name_expr 14 | pos source.Pos 15 | child_nodes []AstNode 16 | // child nodes 17 | ref_tok token.Token 18 | name_tok token.Token 19 | names []token.Token 20 | name string 21 | } 22 | 23 | pub fn new_ref_name_expr(tree &SyntaxTree, ref_tok token.Token, names []token.Token, is_c_name bool) NameExpr { 24 | if ref_tok.kind == .void { 25 | return new_name_expr(tree, names, is_c_name) 26 | } 27 | // name_tok := merge_names(names) 28 | name_tok, name := merge_names(names) 29 | mut child_nodes := [AstNode(ref_tok)] 30 | for n in names { 31 | child_nodes << n 32 | } 33 | return NameExpr{ 34 | tree: tree 35 | ref_tok: ref_tok 36 | name: name 37 | name_tok: name_tok 38 | names: names 39 | pos: source.new_pos_from_pos_bounds(ref_tok.pos, names[names.len - 1].pos) 40 | child_nodes: child_nodes 41 | is_c_name: is_c_name 42 | is_ref: true 43 | } 44 | } 45 | 46 | pub fn new_name_expr(tree &SyntaxTree, names []token.Token, is_c_name bool) NameExpr { 47 | name_tok, name := merge_names(names) 48 | mut child_nodes := []AstNode{} 49 | for n in names { 50 | child_nodes << n 51 | } 52 | return NameExpr{ 53 | tree: tree 54 | ref_tok: token.tok_void 55 | name_tok: name_tok 56 | name: name 57 | names: names 58 | pos: source.new_pos_from_pos_bounds(names[0].pos, names[names.len - 1].pos) 59 | child_nodes: child_nodes 60 | is_c_name: is_c_name 61 | is_ref: false 62 | } 63 | } 64 | 65 | pub fn (ne &NameExpr) child_nodes() []AstNode { 66 | return ne.child_nodes 67 | } 68 | 69 | pub fn (ex NameExpr) text_location() source.TextLocation { 70 | return source.new_text_location(ex.tree.source, ex.pos) 71 | } 72 | 73 | pub fn (ex NameExpr) node_str() string { 74 | return typeof(ex).name 75 | } 76 | 77 | pub fn (ex NameExpr) str() string { 78 | return '$ex.name' 79 | } 80 | 81 | fn merge_names(names []token.Token) (token.Token, string) { 82 | if names.len == 1 { 83 | return names[0], names[0].lit 84 | } 85 | mut b := strings.new_builder(20) 86 | for i, tok in names { 87 | if i != 0 { 88 | b.write_string('.') 89 | } 90 | b.write_string(tok.lit) 91 | } 92 | mut name := b.str() 93 | token := token.Token{ 94 | source: names[0].source 95 | kind: .name 96 | lit: name 97 | pos: source.new_pos_from_pos_bounds(names[0].pos, names[names.len - 1].pos) 98 | } 99 | 100 | return token, name 101 | } 102 | 103 | // fn merge_names(names []token.Token) string { 104 | // if names.len == 1 { 105 | // return names[0].lit 106 | // } 107 | // mut b := strings.new_builder(20) 108 | // for i, tok in names { 109 | // if i != 0 { 110 | // b.write_string('.') 111 | // } 112 | // b.write_string(tok.lit) 113 | // } 114 | // return b.str() 115 | // } 116 | -------------------------------------------------------------------------------- /lib/comp/ast/expr_operator.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | pub const ( 7 | binary_expr_tokens = [token.Kind(token.Kind.plus), .minus, .mul, .div, .amp_amp, .pipe_pipe, 8 | .eq_eq, .exl_mark_eq, .lt, .gt, .lt_eq, .gt_eq, .amp, .pipe, .hat, .lsbr] 9 | unary_expr_tokens = [token.Kind(token.Kind.plus), .minus, .exl_mark, .tilde] 10 | ) 11 | 12 | pub struct BinaryExpr { 13 | pub: 14 | kind SyntaxKind = .binary_expr 15 | pos source.Pos 16 | tree &SyntaxTree 17 | child_nodes []AstNode 18 | 19 | left_expr Expr 20 | op_tok token.Token 21 | right_expr Expr 22 | } 23 | 24 | // new_binary_expr instance an binary expression 25 | // with a left_expr side, right_expr side and operator 26 | pub fn new_binary_expr(tree &SyntaxTree, left_expr Expr, op_tok token.Token, right_expr Expr) BinaryExpr { 27 | if !(op_tok.kind in ast.binary_expr_tokens) { 28 | panic('Expected a binary expresson token, got ($op_tok.kind)') 29 | } 30 | return BinaryExpr{ 31 | tree: tree 32 | left_expr: left_expr 33 | op_tok: op_tok 34 | right_expr: right_expr 35 | pos: source.new_pos_from_pos_bounds(left_expr.pos, right_expr.pos) 36 | child_nodes: [AstNode(left_expr), op_tok, right_expr] 37 | } 38 | } 39 | 40 | pub fn (be &BinaryExpr) child_nodes() []AstNode { 41 | return be.child_nodes 42 | } 43 | 44 | pub fn (ex BinaryExpr) text_location() source.TextLocation { 45 | return source.new_text_location(ex.tree.source, ex.pos) 46 | } 47 | 48 | pub fn (ex BinaryExpr) node_str() string { 49 | return typeof(ex).name 50 | } 51 | 52 | pub fn (ex BinaryExpr) str() string { 53 | return '$ex.left_expr $ex.op_tok.lit $ex.right_expr' 54 | } 55 | 56 | pub struct UnaryExpr { 57 | pub: 58 | // general ast node 59 | tree &SyntaxTree 60 | kind SyntaxKind = .unary_expr 61 | pos source.Pos 62 | child_nodes []AstNode 63 | // child nodes 64 | op_tok token.Token 65 | operand_expr Expr 66 | } 67 | 68 | // new_binary_expr instance an binary expression 69 | // with a left_expr side, right_expr side and operator 70 | pub fn new_unary_expr(tree &SyntaxTree, op_tok token.Token, operand_expr Expr) UnaryExpr { 71 | if !(op_tok.kind in ast.unary_expr_tokens) { 72 | panic('Expected a unary expresson token, got ($op_tok.kind)') 73 | } 74 | return UnaryExpr{ 75 | tree: tree 76 | op_tok: op_tok 77 | operand_expr: operand_expr 78 | pos: source.new_pos_from_pos_bounds(op_tok.pos, operand_expr.pos) 79 | child_nodes: [AstNode(op_tok), operand_expr] 80 | } 81 | } 82 | 83 | pub fn (be &UnaryExpr) child_nodes() []AstNode { 84 | return be.child_nodes 85 | } 86 | 87 | pub fn (ex UnaryExpr) text_location() source.TextLocation { 88 | return source.new_text_location(ex.tree.source, ex.pos) 89 | } 90 | 91 | pub fn (ex UnaryExpr) node_str() string { 92 | return typeof(ex).name 93 | } 94 | 95 | pub fn (ex UnaryExpr) str() string { 96 | return '$ex.op_tok.lit$ex.operand_expr' 97 | } 98 | 99 | pub struct ParaExpr { 100 | pub: 101 | // general ast node 102 | tree &SyntaxTree 103 | kind SyntaxKind = .fn_decl_node 104 | pos source.Pos 105 | child_nodes []AstNode 106 | // child nodes 107 | open_para_token token.Token 108 | expr Expr 109 | close_para_token token.Token 110 | } 111 | 112 | pub fn new_paranthesis_expr(tree &SyntaxTree, open_para_token token.Token, expr Expr, close_para_token token.Token) ParaExpr { 113 | return ParaExpr{ 114 | tree: tree 115 | open_para_token: open_para_token 116 | expr: expr 117 | close_para_token: close_para_token 118 | pos: source.new_pos_from_pos_bounds(open_para_token.pos, close_para_token.pos) 119 | child_nodes: [AstNode(open_para_token), expr, close_para_token] 120 | } 121 | } 122 | 123 | pub fn (pe &ParaExpr) child_nodes() []AstNode { 124 | return pe.child_nodes 125 | } 126 | 127 | pub fn (ex ParaExpr) text_location() source.TextLocation { 128 | return source.new_text_location(ex.tree.source, ex.pos) 129 | } 130 | 131 | pub fn (ex ParaExpr) node_str() string { 132 | return typeof(ex).name 133 | } 134 | 135 | pub fn (ex ParaExpr) str() string { 136 | return '($ex.expr)' 137 | } 138 | -------------------------------------------------------------------------------- /lib/comp/ast/expr_range.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | // Support range expr 7 | // ex: 1..10 8 | pub struct RangeExpr { 9 | pub: 10 | // general ast node 11 | tree &SyntaxTree 12 | kind SyntaxKind = .range_expr 13 | pos source.Pos 14 | child_nodes []AstNode 15 | // child nodes 16 | range token.Token 17 | from_expr Expr 18 | to_expr Expr 19 | } 20 | 21 | pub fn new_range_expr(tree &SyntaxTree, from_expr Expr, range token.Token, to_expr Expr) RangeExpr { 22 | return RangeExpr{ 23 | tree: tree 24 | range: range 25 | from_expr: from_expr 26 | to_expr: to_expr 27 | pos: source.new_pos_from_pos_bounds(range.pos, to_expr.pos) 28 | child_nodes: [AstNode(from_expr), range, to_expr] 29 | } 30 | } 31 | 32 | pub fn (iss &RangeExpr) child_nodes() []AstNode { 33 | return iss.child_nodes 34 | } 35 | 36 | pub fn (ex RangeExpr) text_location() source.TextLocation { 37 | return source.new_text_location(ex.tree.source, ex.pos) 38 | } 39 | 40 | pub fn (ex RangeExpr) node_str() string { 41 | return typeof(ex).name 42 | } 43 | 44 | pub fn (ex RangeExpr) str() string { 45 | return '${ex.from_expr}..$ex.to_expr' 46 | } 47 | -------------------------------------------------------------------------------- /lib/comp/ast/expr_struct_init.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.symbols 5 | import lib.comp.util.source 6 | 7 | pub struct StructInitExpr { 8 | pub: 9 | // general ast node 10 | tree &SyntaxTree 11 | kind SyntaxKind = .struct_init_expr 12 | pos source.Pos 13 | child_nodes []AstNode 14 | // child nodes 15 | name_expr NameExpr 16 | lcbr_tok token.Token 17 | members []StructInitMemberNode 18 | rcbr_tok token.Token 19 | 20 | is_c_init bool 21 | } 22 | 23 | pub fn new_struct_init_expr(tree &SyntaxTree, name_expr NameExpr, lcbr_tok token.Token, members []StructInitMemberNode, rcbr_tok token.Token) StructInitExpr { 24 | mut child_nodes := [AstNode(Expr(name_expr)), lcbr_tok] 25 | for member in members { 26 | child_nodes << member 27 | } 28 | child_nodes << rcbr_tok 29 | return StructInitExpr{ 30 | tree: tree 31 | name_expr: name_expr 32 | members: members 33 | lcbr_tok: lcbr_tok 34 | pos: source.new_pos_from_pos_bounds(name_expr.name_tok.pos, rcbr_tok.pos) 35 | child_nodes: child_nodes 36 | is_c_init: name_expr.names.len > 1 && name_expr.names[0].lit == 'C' 37 | } 38 | } 39 | 40 | pub fn new_struct_init_no_members_expr(typ symbols.TypeSymbol, tree &SyntaxTree) StructInitExpr { 41 | names := typ.name.split('.') 42 | mod_parts := typ.mod.split('.') 43 | 44 | is_c_init := names.len > 1 && names[0] == 'C' 45 | 46 | mut name_toks := []token.Token{} 47 | 48 | if is_c_init { 49 | name_toks << [token.Token{ 50 | kind: .name 51 | lit: 'C' 52 | source: tree.source 53 | }] 54 | } else { 55 | name_toks << [token.Token{ 56 | kind: .name 57 | lit: mod_parts[mod_parts.len - 1] 58 | source: tree.source 59 | }] 60 | } 61 | name_toks << [token.Token{ 62 | kind: .name 63 | lit: names[names.len - 1] 64 | source: tree.source 65 | }] 66 | 67 | name_expr := new_name_expr(tree, name_toks, false) 68 | return StructInitExpr{ 69 | tree: tree 70 | name_expr: name_expr 71 | is_c_init: is_c_init 72 | } 73 | } 74 | 75 | pub fn (iss &StructInitExpr) child_nodes() []AstNode { 76 | return iss.child_nodes 77 | } 78 | 79 | pub fn (ex StructInitExpr) text_location() source.TextLocation { 80 | return source.new_text_location(ex.tree.source, ex.pos) 81 | } 82 | 83 | pub fn (ex StructInitExpr) str() string { 84 | return ex.name_expr.name_tok.lit 85 | } 86 | 87 | pub fn (ex StructInitExpr) node_str() string { 88 | return typeof(ex).name 89 | } 90 | 91 | pub struct StructInitMemberNode { 92 | pub: 93 | // general ast node 94 | tree &SyntaxTree 95 | kind SyntaxKind = .struct_init_mbr_node 96 | pos source.Pos 97 | child_nodes []AstNode 98 | // child nodes 99 | ident token.Token 100 | colon token.Token 101 | expr Expr 102 | } 103 | 104 | pub fn new_init_struct_member_node(tree &SyntaxTree, ident token.Token, colon token.Token, expr Expr) StructInitMemberNode { 105 | return StructInitMemberNode{ 106 | tree: tree 107 | pos: source.new_pos_from_pos_bounds(ident.pos, expr.pos) 108 | child_nodes: [AstNode(ident), colon, expr] 109 | ident: ident 110 | colon: colon 111 | expr: expr 112 | } 113 | } 114 | 115 | pub fn (e &StructInitMemberNode) child_nodes() []AstNode { 116 | return e.child_nodes 117 | } 118 | 119 | pub fn (ex StructInitMemberNode) text_location() source.TextLocation { 120 | return source.new_text_location(ex.tree.source, ex.pos) 121 | } 122 | 123 | pub fn (ex StructInitMemberNode) node_str() string { 124 | return typeof(ex).name 125 | } 126 | -------------------------------------------------------------------------------- /lib/comp/ast/kind.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | // SyntaxKind has all kinds of syntax nodes in the AST 4 | // This is an optimization to check for kinds 5 | // rather than check for type of each node 6 | enum SyntaxKind { 7 | literal_expr 8 | name_expr 9 | unary_expr 10 | binary_expr 11 | para_expr 12 | assign_expr 13 | range_expr 14 | if_expr 15 | call_expr 16 | struct_init_expr 17 | array_init_expr 18 | index_expr 19 | empty_expr 20 | // nodes 21 | operator_node 22 | else_node 23 | else_expr_node 24 | comp_node 25 | syntax_type 26 | param_node 27 | type_node 28 | fn_decl_node 29 | comment_stmt 30 | struct_decl_node 31 | struct_mbr_node 32 | struct_init_mbr_node 33 | call_arg_node 34 | // statements 35 | block_stmt 36 | break_stmt 37 | continue_stmt 38 | global_stmt 39 | expr_stmt 40 | var_decl_stmt 41 | if_stmt 42 | for_stmt 43 | return_stmt 44 | for_range_stmt 45 | assert_stmt 46 | module_stmt 47 | import_stmt 48 | empty 49 | // syntax_helpers 50 | } 51 | -------------------------------------------------------------------------------- /lib/comp/ast/node_comp.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | pub struct CompNode { 7 | pub: 8 | // general ast node 9 | kind SyntaxKind = .comp_node 10 | pos source.Pos 11 | child_nodes []AstNode 12 | // child nodes 13 | eof_tok token.Token 14 | pub mut: 15 | tree &SyntaxTree 16 | members []MemberNode 17 | } 18 | 19 | pub fn (cn &CompNode) child_nodes() []AstNode { 20 | return cn.members.map(AstNode(it)) 21 | } 22 | 23 | pub fn (ex CompNode) text_location() source.TextLocation { 24 | return source.new_text_location(ex.tree.source, ex.pos) 25 | } 26 | 27 | pub fn (ex CompNode) node_str() string { 28 | return typeof(ex).name 29 | } 30 | -------------------------------------------------------------------------------- /lib/comp/ast/node_empty.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | 5 | pub struct EmptyNode { 6 | pub: 7 | // general ast node 8 | kind SyntaxKind = .comp_node 9 | pos source.Pos 10 | child_nodes []AstNode 11 | // child nodes 12 | pub mut: 13 | tree &SyntaxTree = 0 14 | members []MemberNode 15 | } 16 | 17 | pub fn new_empty_node() EmptyNode { 18 | return EmptyNode{} 19 | } 20 | 21 | pub fn (cn &EmptyNode) child_nodes() []AstNode { 22 | return cn.child_nodes 23 | } 24 | 25 | pub fn (ex EmptyNode) text_location() source.TextLocation { 26 | return source.new_text_location(ex.tree.source, ex.pos) 27 | } 28 | 29 | pub fn (ex EmptyNode) node_str() string { 30 | return typeof(ex).name 31 | } 32 | -------------------------------------------------------------------------------- /lib/comp/ast/node_fn_decl.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | pub struct FnDeclNode { 7 | pub: 8 | is_c_decl bool 9 | is_pub bool 10 | // general ast node 11 | tree &SyntaxTree 12 | kind SyntaxKind = .fn_decl_node 13 | pos source.Pos 14 | child_nodes []AstNode 15 | // child nodes 16 | pub_key token.Token 17 | fn_key token.Token 18 | receiver_node ReceiverNode 19 | name_expr NameExpr 20 | lpar_tok token.Token 21 | params SeparatedSyntaxList 22 | rpar_tok token.Token 23 | typ_node TypeNode 24 | 25 | block BlockStmt 26 | } 27 | 28 | pub fn new_empty_fn_decl_node(tree &SyntaxTree) FnDeclNode { 29 | return FnDeclNode{ 30 | tree: tree 31 | } 32 | } 33 | 34 | pub fn new_fn_decl_node(tree &SyntaxTree, pub_key token.Token, fn_key token.Token, receiver_node ReceiverNode, name_expr NameExpr, lpar_tok token.Token, params SeparatedSyntaxList, rpar_tok token.Token, typ_node TypeNode, block BlockStmt) FnDeclNode { 35 | is_pub := pub_key.kind != .void 36 | is_c_decl := name_expr.is_c_name 37 | mut child_nodes := if is_pub { [AstNode(fn_key), Expr(name_expr), lpar_tok] } else { [ 38 | AstNode(pub_key), fn_key, Expr(name_expr), lpar_tok] } 39 | pos := if is_pub { 40 | source.new_pos_from_pos_bounds(fn_key.pos, block.pos) 41 | } else { 42 | source.new_pos_from_pos_bounds(pub_key.pos, block.pos) 43 | } 44 | for i := 0; i < params.len(); i++ { 45 | child_nodes << params.at(i) 46 | } 47 | child_nodes << rpar_tok 48 | child_nodes << typ_node 49 | 50 | return FnDeclNode{ 51 | tree: tree 52 | pos: pos 53 | child_nodes: child_nodes 54 | pub_key: pub_key 55 | fn_key: fn_key 56 | receiver_node: receiver_node 57 | name_expr: name_expr 58 | lpar_tok: lpar_tok 59 | params: params 60 | rpar_tok: rpar_tok 61 | typ_node: typ_node 62 | block: block 63 | is_pub: is_pub 64 | is_c_decl: is_c_decl 65 | } 66 | } 67 | 68 | pub fn (e &FnDeclNode) child_nodes() []AstNode { 69 | return e.child_nodes 70 | } 71 | 72 | pub fn (ex FnDeclNode) text_location() source.TextLocation { 73 | return source.new_text_location(ex.tree.source, ex.pos) 74 | } 75 | 76 | pub fn (ex FnDeclNode) node_str() string { 77 | return typeof(ex).name 78 | } 79 | 80 | pub fn (ex &FnDeclNode) str() string { 81 | return 'fn ${ex.name_expr.name_tok.lit}()' 82 | } 83 | -------------------------------------------------------------------------------- /lib/comp/ast/node_param.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | // TypeNode represents a type identifier 7 | // parses: 8 | // name_tok VarName 9 | // mut name_tok VarName 10 | // name_tok &VarName 11 | pub struct ParamNode { 12 | pub: 13 | // general ast node 14 | tree &SyntaxTree 15 | kind SyntaxKind = .param_node 16 | pos source.Pos 17 | child_nodes []AstNode 18 | // child nodes 19 | name_tok token.Token 20 | variadic_tok token.Token 21 | typ TypeNode 22 | is_mut bool 23 | is_variadic bool 24 | is_ref bool 25 | } 26 | 27 | pub fn new_param_node(tree &SyntaxTree, name_tok token.Token, typ TypeNode, is_mut bool) ParamNode { 28 | return ParamNode{ 29 | tree: tree 30 | pos: source.new_pos_from_pos_bounds(name_tok.pos, typ.name_expr.pos) 31 | child_nodes: [AstNode(name_tok), typ] 32 | name_tok: name_tok 33 | typ: typ 34 | is_mut: is_mut 35 | is_variadic: typ.is_variadic 36 | is_ref: typ.is_ref || is_mut 37 | } 38 | } 39 | 40 | pub fn new_ref_param_node(tree &SyntaxTree, name_tok token.Token, typ TypeNode, is_mut bool) ParamNode { 41 | return ParamNode{ 42 | tree: tree 43 | pos: source.new_pos_from_pos_bounds(name_tok.pos, typ.name_expr.pos) 44 | child_nodes: [AstNode(name_tok), typ] 45 | name_tok: name_tok 46 | typ: typ 47 | is_mut: is_mut 48 | is_variadic: typ.is_variadic 49 | is_ref: true 50 | } 51 | } 52 | 53 | pub fn (e &ParamNode) child_nodes() []AstNode { 54 | return e.child_nodes 55 | } 56 | 57 | pub fn (ex ParamNode) text_location() source.TextLocation { 58 | return source.new_text_location(ex.tree.source, ex.pos) 59 | } 60 | 61 | pub fn (ex ParamNode) node_str() string { 62 | return typeof(ex).name 63 | } 64 | 65 | pub fn (ex ParamNode) str() string { 66 | return '$ex.name_tok.lit $ex.typ.name_expr.name_tok.lit' 67 | } 68 | -------------------------------------------------------------------------------- /lib/comp/ast/node_struct_decl.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import strings 4 | import lib.comp.token 5 | import lib.comp.util.source 6 | 7 | pub struct StructDeclNode { 8 | pub: 9 | // general ast node 10 | tree &SyntaxTree 11 | kind SyntaxKind = .struct_decl_node 12 | pos source.Pos 13 | child_nodes []AstNode 14 | // child nodes 15 | struct_tok token.Token 16 | name_expr NameExpr 17 | lcbr_tok token.Token 18 | members []StructMemberNode 19 | rcbr_tok token.Token 20 | 21 | is_c_decl bool 22 | } 23 | 24 | pub fn new_struct_decl_node(tree &SyntaxTree, struct_tok token.Token, name_expr NameExpr, lcbr_tok token.Token, members []StructMemberNode, rcbr_tok token.Token) StructDeclNode { 25 | mut child_nodes := [AstNode(struct_tok), Expr(name_expr)] 26 | for member in members { 27 | child_nodes << member 28 | } 29 | 30 | return StructDeclNode{ 31 | tree: tree 32 | pos: source.new_pos_from_pos_bounds(struct_tok.pos, name_expr.pos) 33 | child_nodes: child_nodes 34 | struct_tok: struct_tok 35 | name_expr: name_expr 36 | lcbr_tok: lcbr_tok 37 | members: members 38 | rcbr_tok: rcbr_tok 39 | is_c_decl: name_expr.names.len > 1 && name_expr.names[0].lit == 'C' 40 | } 41 | } 42 | 43 | pub fn (e &StructDeclNode) child_nodes() []AstNode { 44 | return e.child_nodes 45 | } 46 | 47 | pub fn (ex StructDeclNode) text_location() source.TextLocation { 48 | return source.new_text_location(ex.tree.source, ex.pos) 49 | } 50 | 51 | pub fn (ex StructDeclNode) node_str() string { 52 | return typeof(ex).name 53 | } 54 | 55 | pub fn (ex StructDeclNode) str() string { 56 | mut b := strings.new_builder(0) 57 | b.writeln('struct $ex.name_expr.name_tok.lit {') 58 | for member in ex.members { 59 | b.writeln(' $member.name_tok.lit $member.type_expr.name_tok.lit') 60 | } 61 | b.writeln('}') 62 | 63 | return b.str() 64 | } 65 | 66 | pub struct StructMemberNode { 67 | pub: 68 | // general ast node 69 | tree &SyntaxTree 70 | kind SyntaxKind = .struct_mbr_node 71 | pos source.Pos 72 | child_nodes []AstNode 73 | // child nodes 74 | name_tok token.Token 75 | ref_tok token.Token 76 | type_expr NameExpr 77 | is_ref bool 78 | has_init bool 79 | init_expr Expr 80 | } 81 | 82 | pub fn new_struct_member_with_init_node(tree &SyntaxTree, name_tok token.Token, ref_tok token.Token, type_expr NameExpr, init_expr Expr) StructMemberNode { 83 | is_ref := ref_tok.kind == .amp 84 | mut child_nodes := [AstNode(name_tok), Expr(type_expr), init_expr] 85 | return StructMemberNode{ 86 | tree: tree 87 | pos: source.new_pos_from_pos_bounds(name_tok.pos, init_expr.pos) 88 | child_nodes: child_nodes 89 | name_tok: name_tok 90 | ref_tok: ref_tok 91 | type_expr: type_expr 92 | init_expr: init_expr 93 | is_ref: is_ref 94 | has_init: true 95 | } 96 | } 97 | 98 | pub fn new_struct_member_node(tree &SyntaxTree, name_tok token.Token, ref_tok token.Token, type_expr NameExpr) StructMemberNode { 99 | is_ref := ref_tok.kind == .amp 100 | mut child_nodes := [AstNode(name_tok), Expr(type_expr)] 101 | return StructMemberNode{ 102 | tree: tree 103 | pos: source.new_pos_from_pos_bounds(name_tok.pos, type_expr.pos) 104 | child_nodes: child_nodes 105 | name_tok: name_tok 106 | ref_tok: ref_tok 107 | type_expr: type_expr 108 | is_ref: is_ref 109 | has_init: false 110 | } 111 | } 112 | 113 | pub fn (e &StructMemberNode) child_nodes() []AstNode { 114 | return e.child_nodes 115 | } 116 | 117 | pub fn (ex StructMemberNode) text_location() source.TextLocation { 118 | return source.new_text_location(ex.tree.source, ex.pos) 119 | } 120 | 121 | pub fn (ex StructMemberNode) node_str() string { 122 | return typeof(ex).name 123 | } 124 | 125 | pub fn (ex StructMemberNode) str() string { 126 | return 'StructMemberNode' 127 | } 128 | -------------------------------------------------------------------------------- /lib/comp/ast/node_type.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | // TypeNode represents a type identifier 7 | // parses: 8 | // VarName 9 | // &VarName 10 | pub struct TypeNode { 11 | pub: 12 | // general ast node 13 | tree &SyntaxTree 14 | kind SyntaxKind = .type_node 15 | pos source.Pos 16 | child_nodes []AstNode 17 | // child nodes 18 | name_expr NameExpr 19 | ref_tok token.Token 20 | variadic_tok token.Token 21 | is_ref bool 22 | is_void bool 23 | is_variadic bool 24 | } 25 | 26 | pub fn new_type_node(tree &SyntaxTree, name_expr NameExpr, variadic_tok token.Token) TypeNode { 27 | is_void := name_expr.names[0].kind == .void 28 | is_variadic := variadic_tok.kind != .void 29 | 30 | return TypeNode{ 31 | tree: tree 32 | pos: name_expr.pos 33 | child_nodes: [AstNode(Expr(name_expr))] 34 | name_expr: name_expr 35 | is_ref: name_expr.is_ref 36 | is_void: is_void 37 | is_variadic: is_variadic 38 | } 39 | } 40 | 41 | pub fn (e &TypeNode) child_nodes() []AstNode { 42 | return e.child_nodes 43 | } 44 | 45 | pub fn (ex TypeNode) text_location() source.TextLocation { 46 | return source.new_text_location(ex.tree.source, ex.pos) 47 | } 48 | 49 | pub fn (ex TypeNode) node_str() string { 50 | return typeof(ex).name 51 | } 52 | 53 | pub fn (ex &TypeNode) str() string { 54 | name := ex.name_expr.name_tok.lit 55 | return name 56 | } -------------------------------------------------------------------------------- /lib/comp/ast/operator_prec.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | 5 | // binary_operator_precedence returns the precedence of operator 6 | pub fn binary_operator_precedence(kind token.Kind) int { 7 | // the precedence of binary operators in order 8 | return match kind { 9 | .div, .mul { 5 } 10 | .plus, .minus { 4 } 11 | .eq_eq, .exl_mark_eq, .gt, .lt, .gt_eq, .lt_eq { 3 } 12 | .amp_amp, .amp { 2 } 13 | .pipe_pipe, .pipe, .hat, .lsbr { 1 } 14 | else { 0 } 15 | } 16 | } 17 | 18 | // binary_operator_precedence returns the precedence of operator 19 | pub fn unary_operator_precedence(kind token.Kind) int { 20 | // the precedence of binary operators in order 21 | return match kind { 22 | .plus, .minus, .exl_mark, .tilde { 7 } 23 | else { 0 } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /lib/comp/ast/receiver_node.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | // ReceiverNode represents a type identifier 7 | // parses: 8 | // VarName 9 | // &VarName 10 | pub struct ReceiverNode { 11 | pub: 12 | // general ast node 13 | tree &SyntaxTree 14 | kind SyntaxKind = .type_node 15 | pos source.Pos 16 | child_nodes []AstNode 17 | // child nodes 18 | lpar_tok token.Token 19 | mut_tok token.Token 20 | name_tok token.Token 21 | typ_node TypeNode 22 | rpar_tok token.Token 23 | is_ref bool 24 | is_empty bool 25 | is_mut bool 26 | } 27 | 28 | pub fn new_empty_receiver_node() ReceiverNode { 29 | return ReceiverNode{tree:&SyntaxTree(0), is_empty: true} 30 | } 31 | 32 | pub fn new_receiver_node(tree &SyntaxTree, lpar_tok token.Token, mut_tok token.Token, name_tok token.Token, typ_node TypeNode, rpar_tok token.Token) ReceiverNode { 33 | is_mut := mut_tok.kind == .key_mut 34 | is_ref := typ_node.is_ref || is_mut 35 | return ReceiverNode{ 36 | tree: tree 37 | pos: source.new_pos_from_pos_bounds(lpar_tok.pos, rpar_tok.pos) 38 | child_nodes: [AstNode(lpar_tok), name_tok, typ_node, rpar_tok] 39 | name_tok: name_tok 40 | typ_node: typ_node 41 | is_ref: is_ref 42 | is_empty: false 43 | is_mut: is_mut 44 | } 45 | } 46 | 47 | pub fn (e &ReceiverNode) child_nodes() []AstNode { 48 | return e.child_nodes 49 | } 50 | 51 | pub fn (ex ReceiverNode) text_location() source.TextLocation { 52 | return source.new_text_location(ex.tree.source, ex.pos) 53 | } 54 | 55 | pub fn (ex ReceiverNode) node_str() string { 56 | return typeof(ex).name 57 | } 58 | 59 | pub fn (ex &ReceiverNode) str() string { 60 | return '$ex.name_tok.lit $ex.typ_node.name_expr.name_tok.lit' 61 | } -------------------------------------------------------------------------------- /lib/comp/ast/sep_syntax_list.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | pub struct SeparatedSyntaxList { 4 | mut: 5 | sep_and_nodes []AstNode 6 | } 7 | 8 | pub fn new_separated_syntax_list(sep_and_nodes []AstNode) SeparatedSyntaxList { 9 | return SeparatedSyntaxList{ 10 | sep_and_nodes: sep_and_nodes 11 | } 12 | } 13 | 14 | pub fn (ssl SeparatedSyntaxList) sep_and_nodes() []AstNode { 15 | return ssl.sep_and_nodes 16 | } 17 | 18 | pub fn (ssl SeparatedSyntaxList) len() int { 19 | return (ssl.sep_and_nodes.len + 1) / 2 20 | } 21 | 22 | pub fn (mut ssl SeparatedSyntaxList) add(expr Expr) { 23 | ssl.sep_and_nodes << expr 24 | } 25 | 26 | pub fn (ssl SeparatedSyntaxList) at(index int) AstNode { 27 | return ssl.sep_and_nodes[index * 2] 28 | } 29 | 30 | pub fn (ssl SeparatedSyntaxList) sep_at(index int) AstNode { 31 | return ssl.sep_and_nodes[index * 2 + 1] 32 | } 33 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_assert.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | import lib.comp.token 5 | 6 | pub struct AssertStmt { 7 | pub: 8 | // general ast node 9 | tree &SyntaxTree 10 | kind SyntaxKind = .assert_stmt 11 | pos source.Pos 12 | child_nodes []AstNode 13 | // child nodes 14 | assert_key token.Token 15 | expr Expr 16 | } 17 | 18 | pub fn new_assert_stmt(tree &SyntaxTree, assert_key token.Token, expr Expr) AssertStmt { 19 | return AssertStmt{ 20 | tree: tree 21 | pos: source.new_pos_from_pos_bounds(assert_key.pos, expr.pos) 22 | child_nodes: [AstNode(assert_key), expr] 23 | assert_key: assert_key 24 | expr: expr 25 | } 26 | } 27 | 28 | pub fn (e &AssertStmt) child_nodes() []AstNode { 29 | return e.child_nodes 30 | } 31 | 32 | pub fn (ex AssertStmt) text_location() source.TextLocation { 33 | return source.new_text_location(ex.tree.source, ex.pos) 34 | } 35 | 36 | pub fn (ex AssertStmt) node_str() string { 37 | return typeof(ex).name 38 | } 39 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_block.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | import strings 6 | 7 | pub struct BlockStmt { 8 | pub: 9 | // general ast node 10 | tree &SyntaxTree 11 | kind SyntaxKind = .block_stmt 12 | pos source.Pos 13 | child_nodes []AstNode 14 | // child nodes 15 | open_brc_tok token.Token 16 | stmts []Stmt 17 | close_brc_tok token.Token 18 | } 19 | 20 | pub fn new_void_block_stmt(tree &SyntaxTree) BlockStmt { 21 | return BlockStmt{ 22 | tree: tree 23 | } 24 | } 25 | 26 | pub fn new_block_stmt(tree &SyntaxTree, open_brc_tok token.Token, stmts []Stmt, close_brc_tok token.Token) BlockStmt { 27 | mut child_nodes := [AstNode(open_brc_tok)] 28 | child_nodes.insert(1, stmts.map(AstNode(it))) 29 | child_nodes << close_brc_tok 30 | 31 | return BlockStmt{ 32 | tree: tree 33 | open_brc_tok: open_brc_tok 34 | stmts: stmts 35 | close_brc_tok: close_brc_tok 36 | child_nodes: child_nodes 37 | pos: source.new_pos_from_pos_bounds(open_brc_tok.pos, close_brc_tok.pos) 38 | } 39 | } 40 | 41 | pub fn (bs &BlockStmt) child_nodes() []AstNode { 42 | return bs.child_nodes 43 | } 44 | 45 | pub fn (ex BlockStmt) text_location() source.TextLocation { 46 | return source.new_text_location(ex.tree.source, ex.pos) 47 | } 48 | 49 | pub fn (ex BlockStmt) node_str() string { 50 | return typeof(ex).name 51 | } 52 | 53 | pub fn (ex BlockStmt) str() string { 54 | mut b := strings.new_builder(0) 55 | b.writeln('{') 56 | for stmt in ex.stmts { 57 | b.writeln(' $stmt') 58 | } 59 | b.writeln('}') 60 | return b.str() 61 | } 62 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_break.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | import lib.comp.token 5 | 6 | pub struct BreakStmt { 7 | pub: 8 | // general ast node 9 | tree &SyntaxTree 10 | kind SyntaxKind = .break_stmt 11 | pos source.Pos 12 | child_nodes []AstNode 13 | // child nodes 14 | break_key token.Token 15 | } 16 | 17 | pub fn new_break_stmt(tree &SyntaxTree, break_key token.Token) BreakStmt { 18 | return BreakStmt{ 19 | tree: tree 20 | pos: break_key.pos 21 | child_nodes: [AstNode(break_key)] 22 | break_key: break_key 23 | } 24 | } 25 | 26 | pub fn (e &BreakStmt) child_nodes() []AstNode { 27 | return e.child_nodes 28 | } 29 | 30 | pub fn (ex BreakStmt) text_location() source.TextLocation { 31 | return source.new_text_location(ex.tree.source, ex.pos) 32 | } 33 | 34 | pub fn (ex BreakStmt) node_str() string { 35 | return typeof(ex).name 36 | } 37 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_comment.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | import lib.comp.token 5 | 6 | pub struct CommentStmt { 7 | pub: 8 | // general ast node 9 | tree &SyntaxTree 10 | kind SyntaxKind = .comment_stmt 11 | pos source.Pos 12 | child_nodes []AstNode 13 | // child nodes 14 | comment_tok token.Token 15 | } 16 | 17 | pub fn new_comment_stmt(tree &SyntaxTree, comment_tok token.Token) CommentStmt { 18 | return CommentStmt{ 19 | tree: tree 20 | pos: comment_tok.pos 21 | child_nodes: [AstNode(comment_tok)] 22 | comment_tok: comment_tok 23 | } 24 | } 25 | 26 | pub fn (e &CommentStmt) child_nodes() []AstNode { 27 | return e.child_nodes 28 | } 29 | 30 | pub fn (ex CommentStmt) text_location() source.TextLocation { 31 | return source.new_text_location(ex.tree.source, ex.pos) 32 | } 33 | 34 | pub fn (ex CommentStmt) node_str() string { 35 | return typeof(ex).name 36 | } 37 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_continue.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | import lib.comp.token 5 | 6 | pub struct ContinueStmt { 7 | pub: 8 | // general ast node 9 | tree &SyntaxTree 10 | kind SyntaxKind = .continue_stmt 11 | pos source.Pos 12 | child_nodes []AstNode 13 | // child nodes 14 | cont_key token.Token 15 | } 16 | 17 | pub fn new_continue_stmt(tree &SyntaxTree, cont_key token.Token) ContinueStmt { 18 | return ContinueStmt{ 19 | tree: tree 20 | pos: cont_key.pos 21 | child_nodes: [AstNode(cont_key)] 22 | cont_key: cont_key 23 | } 24 | } 25 | 26 | pub fn (e &ContinueStmt) child_nodes() []AstNode { 27 | return e.child_nodes 28 | } 29 | 30 | pub fn (ex ContinueStmt) text_location() source.TextLocation { 31 | return source.new_text_location(ex.tree.source, ex.pos) 32 | } 33 | 34 | pub fn (ex ContinueStmt) node_str() string { 35 | return typeof(ex).name 36 | } 37 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_expr.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | 5 | pub struct ExprStmt { 6 | pub: 7 | // general ast node 8 | tree &SyntaxTree 9 | kind SyntaxKind = .expr_stmt 10 | pos source.Pos 11 | child_nodes []AstNode 12 | // child nodes 13 | expr Expr 14 | } 15 | 16 | pub fn new_expr_stmt(tree &SyntaxTree, expr Expr) ExprStmt { 17 | return ExprStmt{ 18 | tree: tree 19 | pos: expr.pos 20 | child_nodes: [AstNode(expr)] 21 | expr: expr 22 | } 23 | } 24 | 25 | pub fn (e &ExprStmt) child_nodes() []AstNode { 26 | return e.child_nodes 27 | } 28 | 29 | pub fn (ex ExprStmt) text_location() source.TextLocation { 30 | return source.new_text_location(ex.tree.source, ex.pos) 31 | } 32 | 33 | pub fn (ex ExprStmt) node_str() string { 34 | return typeof(ex).name 35 | } 36 | 37 | pub fn (ex ExprStmt) str() string { 38 | return '$ex.expr' 39 | } 40 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_for.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | pub struct ForRangeStmt { 7 | pub: 8 | // general ast node 9 | tree &SyntaxTree 10 | kind SyntaxKind = .for_range_stmt 11 | pos source.Pos 12 | child_nodes []AstNode 13 | // child nodes 14 | for_key token.Token 15 | name_tok token.Token 16 | in_key token.Token 17 | range_expr Expr 18 | body_stmt Stmt 19 | } 20 | 21 | pub fn new_for_range_stmt(tree &SyntaxTree, for_key token.Token, name_tok token.Token, in_key token.Token, range_expr Expr, body_stmt Stmt) ForRangeStmt { 22 | return ForRangeStmt{ 23 | tree: tree 24 | pos: source.new_pos_from_pos_bounds(for_key.pos, body_stmt.pos) 25 | child_nodes: [AstNode(for_key), name_tok, in_key, range_expr, body_stmt] 26 | for_key: for_key 27 | name_tok: name_tok 28 | in_key: in_key 29 | range_expr: range_expr 30 | body_stmt: body_stmt 31 | } 32 | } 33 | 34 | pub fn (e &ForRangeStmt) child_nodes() []AstNode { 35 | return e.child_nodes 36 | } 37 | 38 | pub fn (ex ForRangeStmt) text_location() source.TextLocation { 39 | return source.new_text_location(ex.tree.source, ex.pos) 40 | } 41 | 42 | pub fn (ex ForRangeStmt) node_str() string { 43 | return typeof(ex).name 44 | } 45 | 46 | pub fn (ex ForRangeStmt) str() string { 47 | return 'for $ex.name_tok.lit in $ex.range_expr $ex.body_stmt' 48 | } 49 | 50 | pub struct ForStmt { 51 | pub: 52 | has_cond bool 53 | // general ast node 54 | tree &SyntaxTree 55 | kind SyntaxKind = .for_stmt 56 | pos source.Pos 57 | child_nodes []AstNode 58 | // child nodes 59 | for_key token.Token 60 | cond_expr Expr 61 | body_stmt Stmt 62 | } 63 | 64 | pub fn (fs &ForStmt) child_nodes() []AstNode { 65 | return fs.child_nodes 66 | } 67 | 68 | pub fn new_for_stmt(tree &SyntaxTree, for_key token.Token, cond_expr Expr, body_stmt Stmt, has_cond bool) ForStmt { 69 | return ForStmt{ 70 | tree: tree 71 | for_key: for_key 72 | cond_expr: cond_expr 73 | body_stmt: body_stmt 74 | has_cond: has_cond 75 | pos: source.new_pos_from_pos_bounds(for_key.pos, body_stmt.pos) 76 | child_nodes: [AstNode(for_key), cond_expr, body_stmt] 77 | } 78 | } 79 | 80 | pub fn (iss ForStmt) node_str() string { 81 | return typeof(iss).name 82 | } 83 | 84 | pub fn (ex ForStmt) text_location() source.TextLocation { 85 | return source.new_text_location(ex.tree.source, ex.pos) 86 | } 87 | 88 | pub fn (iss ForStmt) str() string { 89 | if iss.has_cond { 90 | return 'for $iss.cond_expr $iss.body_stmt' 91 | } else { 92 | return 'for $iss.body_stmt' 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_global.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | 5 | pub struct GlobStmt { 6 | pub: 7 | // general ast node 8 | tree &SyntaxTree 9 | kind SyntaxKind = .global_stmt 10 | pos source.Pos 11 | child_nodes []AstNode 12 | // child nodes 13 | stmt Stmt 14 | } 15 | 16 | pub fn new_glob_stmt(tree &SyntaxTree, stmt Stmt) GlobStmt { 17 | return GlobStmt{ 18 | tree: tree 19 | pos: stmt.pos 20 | child_nodes: [AstNode(stmt)] 21 | stmt: stmt 22 | } 23 | } 24 | 25 | pub fn (e &GlobStmt) child_nodes() []AstNode { 26 | return e.child_nodes 27 | } 28 | 29 | pub fn (ex GlobStmt) text_location() source.TextLocation { 30 | return source.new_text_location(ex.tree.source, ex.pos) 31 | } 32 | 33 | pub fn (ex GlobStmt) node_str() string { 34 | return typeof(ex).name 35 | } 36 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_if.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | pub struct IfStmt { 7 | pub: 8 | has_else bool 9 | // general ast node 10 | tree &SyntaxTree 11 | kind SyntaxKind = .if_stmt 12 | pos source.Pos 13 | child_nodes []AstNode 14 | // child nodes 15 | if_key token.Token 16 | key_else_tok token.Token 17 | cond_expr Expr 18 | then_stmt Stmt 19 | else_stmt Stmt 20 | } 21 | 22 | pub fn new_if_else_stmt(tree &SyntaxTree, if_key token.Token, cond_expr Expr, then_stmt Stmt, key_else_tok token.Token, else_stmt Stmt) IfStmt { 23 | return IfStmt{ 24 | tree: tree 25 | if_key: if_key 26 | key_else_tok: key_else_tok 27 | cond_expr: cond_expr 28 | has_else: true 29 | then_stmt: then_stmt 30 | else_stmt: else_stmt 31 | pos: source.new_pos_from_pos_bounds(if_key.pos, else_stmt.pos) 32 | child_nodes: [AstNode(if_key), cond_expr, then_stmt, key_else_tok, else_stmt] 33 | } 34 | } 35 | 36 | pub fn new_if_stmt(tree &SyntaxTree, if_key token.Token, cond_expr Expr, then_stmt Stmt) IfStmt { 37 | return IfStmt{ 38 | tree: tree 39 | if_key: if_key 40 | cond_expr: cond_expr 41 | has_else: false 42 | then_stmt: then_stmt 43 | pos: source.new_pos_from_pos_bounds(if_key.pos, then_stmt.pos) 44 | child_nodes: [AstNode(if_key), cond_expr, then_stmt] 45 | } 46 | } 47 | 48 | pub fn (iss &IfStmt) child_nodes() []AstNode { 49 | return iss.child_nodes 50 | } 51 | 52 | pub fn (ex IfStmt) text_location() source.TextLocation { 53 | return source.new_text_location(ex.tree.source, ex.pos) 54 | } 55 | 56 | pub fn (iss IfStmt) node_str() string { 57 | return typeof(iss).name 58 | } 59 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_import.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | import lib.comp.token 5 | 6 | pub struct ImportStmt { 7 | pub: 8 | // general ast node 9 | tree &SyntaxTree 10 | kind SyntaxKind = .import_stmt 11 | pos source.Pos 12 | child_nodes []AstNode 13 | // child nodes 14 | import_key token.Token 15 | name_expr NameExpr 16 | } 17 | 18 | pub fn new_import_stmt(tree &SyntaxTree, import_key token.Token, name_expr NameExpr) ImportStmt { 19 | return ImportStmt{ 20 | tree: tree 21 | pos: source.new_pos_from_pos_bounds(import_key.pos, name_expr.name_tok.pos) 22 | child_nodes: [AstNode(import_key), name_expr.name_tok] 23 | import_key: import_key 24 | name_expr: name_expr 25 | } 26 | } 27 | 28 | pub fn (e &ImportStmt) child_nodes() []AstNode { 29 | return e.child_nodes 30 | } 31 | 32 | pub fn (ex ImportStmt) text_location() source.TextLocation { 33 | return source.new_text_location(ex.tree.source, ex.pos) 34 | } 35 | 36 | pub fn (ex ImportStmt) node_str() string { 37 | return typeof(ex).name 38 | } 39 | 40 | pub fn (ex ImportStmt) str() string { 41 | return typeof(ex).name 42 | } 43 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_module.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | import lib.comp.token 5 | 6 | pub struct ModuleStmt { 7 | pub: 8 | // general ast node 9 | tree &SyntaxTree 10 | kind SyntaxKind = .module_stmt 11 | pos source.Pos 12 | child_nodes []AstNode 13 | // child nodes 14 | module_key token.Token 15 | name_tok token.Token 16 | } 17 | 18 | pub fn new_module_stmt(tree &SyntaxTree, module_key token.Token, name_tok token.Token) ModuleStmt { 19 | return ModuleStmt{ 20 | tree: tree 21 | pos: source.new_pos_from_pos_bounds(module_key.pos, name_tok.pos) 22 | child_nodes: [AstNode(module_key), name_tok] 23 | module_key: module_key 24 | name_tok: name_tok 25 | } 26 | } 27 | 28 | pub fn (e &ModuleStmt) child_nodes() []AstNode { 29 | return e.child_nodes 30 | } 31 | 32 | pub fn (ex ModuleStmt) text_location() source.TextLocation { 33 | return source.new_text_location(ex.tree.source, ex.pos) 34 | } 35 | 36 | pub fn (ex ModuleStmt) node_str() string { 37 | return typeof(ex).name 38 | } 39 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_return.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.util.source 4 | import lib.comp.token 5 | 6 | pub struct ReturnStmt { 7 | pub: 8 | // general ast node 9 | tree &SyntaxTree 10 | kind SyntaxKind = .return_stmt 11 | pos source.Pos 12 | child_nodes []AstNode 13 | // child nodes 14 | has_expr bool 15 | return_key token.Token 16 | expr Expr 17 | } 18 | 19 | pub fn new_return_with_expr_stmt(tree &SyntaxTree, return_key token.Token, expr Expr) ReturnStmt { 20 | return ReturnStmt{ 21 | tree: tree 22 | pos: return_key.pos 23 | child_nodes: [AstNode(return_key), expr] 24 | return_key: return_key 25 | expr: expr 26 | has_expr: true 27 | } 28 | } 29 | 30 | pub fn new_return_stmt(tree &SyntaxTree, return_key token.Token) ReturnStmt { 31 | return ReturnStmt{ 32 | tree: tree 33 | pos: return_key.pos 34 | child_nodes: [AstNode(return_key)] 35 | return_key: return_key 36 | has_expr: false 37 | } 38 | } 39 | 40 | pub fn (e &ReturnStmt) child_nodes() []AstNode { 41 | return e.child_nodes 42 | } 43 | 44 | pub fn (ex ReturnStmt) text_location() source.TextLocation { 45 | return source.new_text_location(ex.tree.source, ex.pos) 46 | } 47 | 48 | pub fn (ex ReturnStmt) node_str() string { 49 | return typeof(ex).name 50 | } 51 | -------------------------------------------------------------------------------- /lib/comp/ast/stmt_var_decl.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import lib.comp.token 4 | import lib.comp.util.source 5 | 6 | pub struct VarDeclStmt { 7 | pub: 8 | tree &SyntaxTree 9 | kind SyntaxKind = .var_decl_stmt 10 | child_nodes []AstNode 11 | is_mut bool 12 | 13 | mut_tok token.Token 14 | ident NameExpr 15 | eq_tok token.Token 16 | expr Expr 17 | pos source.Pos 18 | } 19 | 20 | pub fn new_var_decl_stmt(tree &SyntaxTree, mut_tok token.Token, ident NameExpr, eq_tok token.Token, expr Expr) VarDeclStmt { 21 | is_mut := mut_tok.kind == .key_mut 22 | child_nodes := if !is_mut { [AstNode(ident.name_tok), eq_tok, expr] } else { [AstNode(mut_tok), 23 | ident.name_tok, eq_tok, expr] } 24 | pos := if !is_mut { 25 | source.new_pos_from_pos_bounds(ident.name_tok.pos, expr.pos) 26 | } else { 27 | source.new_pos_from_pos_bounds(mut_tok.pos, expr.pos) 28 | } 29 | return VarDeclStmt{ 30 | tree: tree 31 | mut_tok: mut_tok 32 | ident: ident 33 | expr: expr 34 | eq_tok: eq_tok 35 | is_mut: is_mut 36 | pos: pos 37 | child_nodes: child_nodes 38 | } 39 | } 40 | 41 | pub fn (ae &VarDeclStmt) child_nodes() []AstNode { 42 | return ae.child_nodes 43 | } 44 | 45 | pub fn (ex VarDeclStmt) text_location() source.TextLocation { 46 | return source.new_text_location(ex.tree.source, ex.pos) 47 | } 48 | 49 | pub fn (ex VarDeclStmt) node_str() string { 50 | return typeof(ex).name 51 | } 52 | 53 | pub fn (ex VarDeclStmt) str() string { 54 | return typeof(ex).name 55 | } 56 | -------------------------------------------------------------------------------- /lib/comp/ast/syntax_tree.v: -------------------------------------------------------------------------------- 1 | module ast 2 | 3 | import os 4 | import lib.comp.util.source as src 5 | 6 | [heap] 7 | pub struct SyntaxTree { 8 | pub: 9 | source &src.SourceText // represents source code 10 | pub mut: 11 | log &src.Diagnostics // errors when parsing 12 | root CompNode 13 | mod string 14 | imports []ImportStmt // imports of the file 15 | } 16 | 17 | pub fn new_syntax_tree(text string) &SyntaxTree { 18 | source := src.new_source_text_from_file(text, '') 19 | log := src.new_diagonistics() 20 | return &SyntaxTree{ 21 | log: log 22 | source: source 23 | } 24 | } 25 | 26 | pub fn new_syntax_tree_from_file(filename string) ?&SyntaxTree { 27 | text := os.read_file(filename) ? 28 | source := src.new_source_text_from_file(text, filename) 29 | log := src.new_diagonistics() 30 | return &SyntaxTree{ 31 | log: log 32 | source: source 33 | } 34 | } 35 | 36 | pub fn (t SyntaxTree) str() string { 37 | return '' 38 | } 39 | -------------------------------------------------------------------------------- /lib/comp/ast/walker/print.v: -------------------------------------------------------------------------------- 1 | module walker 2 | 3 | import strings 4 | import term 5 | import lib.comp.ast 6 | 7 | struct NodePrinter { 8 | mut: 9 | tree []string 10 | } 11 | 12 | pub fn print_expression(node ast.Node) string { 13 | p := NodePrinter{} 14 | walk_tree(p, node) 15 | mut b := strings.new_builder(0) 16 | for s in p.tree { 17 | b.write_string(s) 18 | } 19 | return b.str() 20 | } 21 | 22 | fn (mut p NodePrinter) visit_tree(node ast.Node, last_child bool, indent string) ?string { 23 | mut b := strings.new_builder(0) 24 | 25 | marker := if last_child { '└──' } else { '├──' } 26 | 27 | b.write_string(term.gray(indent)) 28 | if indent.len > 0 { 29 | b.write_string(term.gray(marker)) 30 | } 31 | new_ident := indent + if last_child { ' ' } else { '│ ' } 32 | mut node_str := node.node_str() 33 | if node_str[0] == `&` { 34 | node_str = node_str[5..] 35 | } 36 | 37 | b.writeln(term.gray(node_str)) 38 | match node { 39 | ast.Expr { 40 | if node is ast.LiteralExpr { 41 | b.writeln(term.bright_cyan(' $node.val')) 42 | } else if node is ast.BinaryExpr { 43 | b.writeln(term.bright_cyan(' $node.op_tok.kind')) 44 | } else if node is ast.NameExpr { 45 | b.writeln(term.bright_cyan(' ${node.name}')) 46 | } else if node is ast.UnaryExpr { 47 | b.writeln(term.bright_cyan(' $node.op_tok.kind')) 48 | } else { 49 | b.writeln('') 50 | } 51 | } 52 | ast.Stmt { 53 | if node is ast.VarDeclStmt { 54 | b.writeln(term.bright_cyan(' $node.ident.name_tok.lit')) 55 | } else { 56 | b.writeln('') 57 | } 58 | } 59 | else {} 60 | } 61 | 62 | p.tree << b.str() 63 | return new_ident 64 | } 65 | -------------------------------------------------------------------------------- /lib/comp/ast/walker/walker.v: -------------------------------------------------------------------------------- 1 | module walker 2 | 3 | import lib.comp.ast 4 | 5 | // pub type InspectorFn = fn (node ast.Node, data voidptr) bool 6 | // Visitor defines a visit method which is invoked by the walker in each node it encounters. 7 | pub interface Visitor { 8 | visit(node ast.Node) ? 9 | } 10 | 11 | pub interface VisitorTree { 12 | visit_tree(node ast.Node, last_child bool, tree_info string) ?string 13 | } 14 | 15 | 16 | // struct Inspector { 17 | // inspector_callback InspectorFn 18 | // mut: 19 | // data voidptr 20 | // } 21 | 22 | // pub fn (i &Inspector) visit(node ast.Node) ? { 23 | // if i.inspector_callback(node, i.data) { 24 | // return 25 | // } 26 | // return none 27 | // } 28 | 29 | // // inspect traverses and checks the AST node on a depth-first order and based on the data given 30 | // pub fn inspect(node ast.Node, data voidptr, inspector_callback InspectorFn) { 31 | // walk(Inspector{inspector_callback, data}, node) 32 | // } 33 | 34 | // walk traverses the AST using the given visitor 35 | pub fn walk(visitor Visitor, node ast.Node) { 36 | visitor.visit(node) or { return } 37 | children := node.child_nodes() 38 | for child_node in children { 39 | walk(visitor, &child_node) 40 | } 41 | } 42 | 43 | fn walk_tree_recursive(visitor VisitorTree, node ast.Node, last_child bool, tree_info string) { 44 | t_info := visitor.visit_tree(node, last_child, tree_info) or { return } 45 | children := node.child_nodes() 46 | for i, child_node in children { 47 | walk_tree_recursive(visitor, &child_node, i == children.len - 1, t_info) 48 | } 49 | } 50 | 51 | pub fn walk_tree(visitor VisitorTree, node ast.Node) { 52 | walk_tree_recursive(visitor, node, true, '') 53 | } 54 | -------------------------------------------------------------------------------- /lib/comp/binding/convertion/conversion.v: -------------------------------------------------------------------------------- 1 | module convertion 2 | 3 | import lib.comp.symbols 4 | 5 | pub const ( 6 | conv_none = new_convertion(false, false, false) 7 | conv_ident = new_convertion(true, true, true) 8 | conv_implicit = new_convertion(true, false, true) 9 | conv_explicit = new_convertion(true, false, false) 10 | ) 11 | 12 | struct Convertion { 13 | pub: 14 | exists bool 15 | is_identity bool 16 | is_explicit bool 17 | is_implicit bool 18 | } 19 | 20 | fn new_convertion(exists bool, is_identity bool, is_implicit bool) Convertion { 21 | return Convertion{ 22 | exists: exists 23 | is_identity: is_identity 24 | is_implicit: is_implicit 25 | is_explicit: exists && !is_implicit 26 | } 27 | } 28 | 29 | pub fn classify(from symbols.TypeSymbol, to symbols.TypeSymbol) Convertion { 30 | if from.kind != .void_symbol && to.kind == .any_symbol { 31 | return convertion.conv_implicit 32 | } 33 | if from.kind == .any_symbol && to.kind != .void_symbol { 34 | return convertion.conv_explicit 35 | } 36 | 37 | if from is symbols.StructTypeSymbol { 38 | if to is symbols.StructTypeSymbol { 39 | if from == to { 40 | return convertion.conv_ident 41 | } 42 | } else { 43 | if to.kind == .string_symbol && to.name == 'string' { 44 | return convertion.conv_implicit 45 | } 46 | } 47 | } else { 48 | if from.kind == to.kind { 49 | return convertion.conv_ident 50 | } 51 | if from.kind == .bool_symbol || from.kind == .int_symbol || from.kind == .i64_symbol{ 52 | if to.kind == .string_symbol { 53 | return convertion.conv_explicit 54 | } 55 | } 56 | if from.kind == .string_symbol { 57 | if to.kind == .bool_symbol || to.kind == .int_symbol { 58 | return convertion.conv_explicit 59 | } 60 | if to.name == 'String' { 61 | return convertion.conv_implicit 62 | } 63 | } 64 | if from.kind == .byte_symbol { 65 | if to.kind == .char_symbol { 66 | return convertion.conv_explicit 67 | } 68 | } 69 | if from.kind == .char_symbol { 70 | if to.kind == .byte_symbol { 71 | return convertion.conv_explicit 72 | } 73 | } 74 | if from.kind == .int_symbol { 75 | if to.kind == .i64_symbol { 76 | return convertion.conv_implicit 77 | } 78 | if to is symbols.StructTypeSymbol { 79 | if to.is_ref { 80 | return convertion.conv_explicit 81 | } 82 | } 83 | } 84 | } 85 | 86 | return convertion.conv_none 87 | } 88 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_array_init.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundArrayInitExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .sruct_init_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | // child nodes 12 | exprs []BoundExpr 13 | } 14 | 15 | pub fn new_bound_val_array_init_expr(typ symbols.TypeSymbol, exprs []BoundExpr) BoundExpr { 16 | return BoundArrayInitExpr{ 17 | typ: typ 18 | exprs: exprs 19 | } 20 | } 21 | 22 | pub fn (ex BoundArrayInitExpr) node_str() string { 23 | return 'typeof(ex).name' 24 | } 25 | 26 | pub fn (ex BoundArrayInitExpr) str() string { 27 | return '[]$ex.typ.name' 28 | } 29 | 30 | pub fn (ex BoundArrayInitExpr) to_ref_type() BoundArrayInitExpr { 31 | return BoundArrayInitExpr{ 32 | ...ex 33 | typ: ex.typ.to_ref_type() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_assign.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.token 4 | import lib.comp.symbols 5 | 6 | pub struct BoundAssignExpr { 7 | pub: 8 | // general bound node 9 | kind BoundNodeKind = .assign_expr 10 | typ symbols.TypeSymbol 11 | child_nodes []BoundNode 12 | // child nodes 13 | var symbols.VariableSymbol 14 | names []token.Token 15 | expr BoundExpr 16 | } 17 | 18 | pub fn new_bound_assign_expr(var symbols.VariableSymbol, expr BoundExpr) BoundExpr { 19 | return BoundAssignExpr{ 20 | child_nodes: [BoundNode(expr)] 21 | var: var 22 | typ: expr.typ 23 | expr: expr 24 | } 25 | } 26 | 27 | pub fn new_bound_assign_with_names_expr(var symbols.VariableSymbol, names []token.Token, expr BoundExpr) BoundExpr { 28 | return BoundAssignExpr{ 29 | child_nodes: [BoundNode(expr)] 30 | var: var 31 | names: names 32 | typ: expr.typ 33 | expr: expr 34 | } 35 | } 36 | 37 | pub fn (ex BoundAssignExpr) node_str() string { 38 | return typeof(ex).name 39 | } 40 | 41 | pub fn (ex BoundAssignExpr) str() string { 42 | return '$ex.var.name = $ex.expr' 43 | } 44 | 45 | pub fn (ex BoundAssignExpr) to_ref_type() BoundAssignExpr { 46 | return BoundAssignExpr{ 47 | ...ex 48 | typ: ex.typ.to_ref_type() 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_binary.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | import lib.comp.token 5 | 6 | pub struct BoundBinaryExpr { 7 | pub: 8 | // general bound node 9 | kind BoundNodeKind = .binary_expr 10 | typ symbols.TypeSymbol 11 | child_nodes []BoundNode 12 | // child nodes 13 | left_expr BoundExpr 14 | op BoundBinaryOperator 15 | right_expr BoundExpr 16 | } 17 | 18 | pub fn new_bound_binary_expr(left_expr BoundExpr, op BoundBinaryOperator, right_expr BoundExpr) BoundExpr { 19 | return BoundBinaryExpr{ 20 | typ: op.res_typ 21 | op: op 22 | left_expr: left_expr 23 | right_expr: right_expr 24 | child_nodes: [BoundNode(left_expr), right_expr] 25 | } 26 | } 27 | 28 | pub fn (ex BoundBinaryExpr) node_str() string { 29 | return typeof(ex).name 30 | } 31 | 32 | pub fn (ex BoundBinaryExpr) str() string { 33 | return '$ex.left_expr ${token.token_str[ex.op.kind]} $ex.right_expr' 34 | } 35 | 36 | pub fn (ex BoundBinaryExpr) to_ref_type() BoundBinaryExpr { 37 | return BoundBinaryExpr{ 38 | ...ex 39 | typ: ex.typ.to_ref_type() 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_call.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundCallExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .call_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | is_c_call bool 12 | // child nodes 13 | func symbols.FunctionSymbol 14 | receiver symbols.LocalVariableSymbol 15 | 16 | params []BoundExpr 17 | } 18 | 19 | pub fn new_bound_call_expr(func symbols.FunctionSymbol, receiver symbols.LocalVariableSymbol, params []BoundExpr, is_c_call bool) BoundExpr { 20 | return BoundCallExpr{ 21 | typ: func.typ 22 | is_c_call: is_c_call 23 | func: func 24 | receiver: receiver 25 | params: params 26 | } 27 | } 28 | 29 | pub fn (ex BoundCallExpr) node_str() string { 30 | return typeof(ex).name 31 | } 32 | 33 | pub fn (ex BoundCallExpr) str() string { 34 | // TODO: Gen parameters 35 | return 'fn ${ex.func.name}()' 36 | } 37 | 38 | pub fn (ex BoundCallExpr) to_ref_type() BoundCallExpr { 39 | return BoundCallExpr{ 40 | ...ex 41 | typ: ex.typ.to_ref_type() 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_conv.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundConvExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .conv_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | // child nodes 12 | expr BoundExpr 13 | } 14 | 15 | pub fn new_bound_conv_expr(typ symbols.TypeSymbol, expr BoundExpr) BoundExpr { 16 | return BoundConvExpr{ 17 | child_nodes: [BoundNode(expr)] 18 | typ: typ 19 | expr: expr 20 | } 21 | } 22 | 23 | pub fn (ex BoundConvExpr) node_str() string { 24 | return typeof(ex).name 25 | } 26 | 27 | pub fn (ex BoundConvExpr) str() string { 28 | return '${ex.typ.name}($ex.expr)' 29 | } 30 | 31 | pub fn (ex BoundConvExpr) to_ref_type() BoundConvExpr { 32 | return BoundConvExpr{ 33 | ...ex 34 | typ: ex.typ.to_ref_type() 35 | } 36 | } 37 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_empty.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundNoneExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .error_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | } 12 | 13 | pub fn new_bound_emtpy_expr() BoundExpr { 14 | return BoundNoneExpr{ 15 | typ: symbols.error_symbol 16 | } 17 | } 18 | 19 | pub fn (ex BoundNoneExpr) node_str() string { 20 | return typeof(ex).name 21 | } 22 | 23 | pub fn (ex BoundNoneExpr) str() string { 24 | return '' 25 | } 26 | 27 | pub fn (ex BoundNoneExpr) to_ref_type() BoundNoneExpr { 28 | return BoundNoneExpr{ 29 | ...ex 30 | typ: ex.typ.to_ref_type() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_error.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundErrorExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .error_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | } 12 | 13 | pub fn new_bound_error_expr() BoundExpr { 14 | return BoundErrorExpr{ 15 | typ: symbols.error_symbol 16 | } 17 | } 18 | 19 | pub fn (ex BoundErrorExpr) node_str() string { 20 | return typeof(ex).name 21 | } 22 | 23 | pub fn (ex BoundErrorExpr) str() string { 24 | return '?' 25 | } 26 | 27 | pub fn (ex BoundErrorExpr) to_ref_type() BoundErrorExpr { 28 | return BoundErrorExpr{ 29 | ...ex 30 | typ: ex.typ.to_ref_type() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_if.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundIfExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .if_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | // child nodes 12 | cond_expr BoundExpr 13 | then_stmt BoundStmt 14 | else_stmt BoundStmt 15 | } 16 | 17 | pub fn new_if_else_expr(cond_expr BoundExpr, then_stmt BoundStmt, else_stmt BoundStmt) BoundExpr { 18 | // get last expression 19 | block := then_stmt as BoundBlockStmt 20 | last_expr := (block.child_nodes.last() as BoundStmt) as BoundExprStmt 21 | expr_typ := last_expr.expr.typ 22 | return BoundIfExpr{ 23 | child_nodes: [BoundNode(cond_expr), then_stmt, else_stmt] 24 | cond_expr: cond_expr 25 | typ: expr_typ 26 | then_stmt: then_stmt 27 | else_stmt: else_stmt 28 | } 29 | } 30 | 31 | pub fn (ex BoundIfExpr) node_str() string { 32 | return typeof(ex).name 33 | } 34 | 35 | pub fn (ex BoundIfExpr) str() string { 36 | return 'if $ex.cond_expr { $ex.then_stmt } else { $ex.else_stmt }' 37 | } 38 | 39 | pub fn (ex BoundIfExpr) to_ref_type() BoundIfExpr { 40 | return BoundIfExpr{ 41 | ...ex 42 | typ: ex.typ.to_ref_type() 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_index.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundIndexExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .index_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | // child nodes 12 | left_expr BoundExpr 13 | index_expr BoundExpr 14 | } 15 | 16 | pub fn new_bound_index_expr(left_expr BoundExpr, index_expr BoundExpr) BoundExpr { 17 | return BoundIndexExpr{ 18 | child_nodes: [BoundNode(left_expr), index_expr] 19 | typ: index_expr.typ 20 | left_expr: left_expr 21 | index_expr: index_expr 22 | } 23 | } 24 | 25 | pub fn (ex BoundIndexExpr) node_str() string { 26 | return typeof(ex).name 27 | } 28 | 29 | pub fn (ex BoundIndexExpr) str() string { 30 | return '[$ex.index_expr]' 31 | } 32 | 33 | pub fn (ex BoundIndexExpr) to_ref_type() BoundIndexExpr { 34 | return BoundIndexExpr{ 35 | ...ex 36 | typ: ex.typ.to_ref_type() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_literal.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundLiteralExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .literal_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | // child nodes 12 | const_val symbols.ConstSymbol 13 | } 14 | 15 | pub fn new_bound_literal_expr(val symbols.LitVal) BoundExpr { 16 | return BoundLiteralExpr{ 17 | typ: val.typ() 18 | const_val: symbols.new_const_symbol(val) 19 | } 20 | } 21 | 22 | pub fn (ex BoundLiteralExpr) node_str() string { 23 | return 'typeof(ex).name' 24 | } 25 | 26 | pub fn (ex BoundLiteralExpr) str() string { 27 | return '$ex.const_val.val' 28 | } 29 | 30 | pub fn (ex BoundLiteralExpr) to_ref_type() BoundLiteralExpr { 31 | return BoundLiteralExpr{ 32 | ...ex 33 | typ: ex.typ.to_ref_type() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_none.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct NoneExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .none_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | } 12 | 13 | pub fn new_empty_expr() BoundExpr { 14 | return NoneExpr{ 15 | typ: symbols.none_symbol 16 | } 17 | } 18 | 19 | pub fn (ex NoneExpr) node_str() string { 20 | return 'typeof(ex).name' 21 | } 22 | 23 | pub fn (ex NoneExpr) str() string { 24 | return '' 25 | } 26 | 27 | pub fn (ex NoneExpr) to_ref_type() NoneExpr { 28 | return NoneExpr{ 29 | ...ex 30 | typ: ex.typ.to_ref_type() 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_range.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundRangeExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .range_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | // child nodes 12 | from_expr BoundExpr 13 | to_expr BoundExpr 14 | } 15 | 16 | fn new_range_expr(from_expr BoundExpr, to_expr BoundExpr) BoundExpr { 17 | return BoundRangeExpr{ 18 | child_nodes: [BoundNode(from_expr), to_expr] 19 | typ: from_expr.typ 20 | from_expr: from_expr 21 | to_expr: to_expr 22 | } 23 | } 24 | 25 | pub fn (ex BoundRangeExpr) node_str() string { 26 | return typeof(ex).name 27 | } 28 | 29 | pub fn (ex BoundRangeExpr) str() string { 30 | return '${ex.from_expr}..$ex.to_expr' 31 | } 32 | 33 | pub fn (ex BoundRangeExpr) to_ref_type() BoundRangeExpr { 34 | return BoundRangeExpr{ 35 | ...ex 36 | typ: ex.typ.to_ref_type() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_struct_init.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundStructInitExpr { 6 | pub: 7 | // general bound node 8 | kind BoundNodeKind = .sruct_init_expr 9 | typ symbols.TypeSymbol 10 | child_nodes []BoundNode 11 | // child nodes 12 | members []BoundStructInitMember 13 | } 14 | 15 | pub fn new_bound_struct_init_expr(typ symbols.TypeSymbol, members []BoundStructInitMember) BoundExpr { 16 | return BoundStructInitExpr{ 17 | typ: typ 18 | members: members 19 | } 20 | } 21 | 22 | pub fn (ex BoundStructInitExpr) node_str() string { 23 | return 'typeof(ex).name' 24 | } 25 | 26 | pub fn (ex BoundStructInitExpr) str() string { 27 | return '$ex.typ.name{}' 28 | } 29 | 30 | pub fn (ex BoundStructInitExpr) to_ref_type() BoundStructInitExpr { 31 | return BoundStructInitExpr{ 32 | ...ex 33 | typ: ex.typ.to_ref_type() 34 | } 35 | } 36 | 37 | pub struct BoundStructInitMember { 38 | pub: 39 | name string 40 | typ symbols.TypeSymbol 41 | expr BoundExpr 42 | } 43 | 44 | pub fn new_bound_struct_init_member(name string, expr BoundExpr) BoundStructInitMember { 45 | return BoundStructInitMember{ 46 | typ: expr.typ 47 | name: name 48 | expr: expr 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_unary.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | import lib.comp.token 5 | 6 | pub struct BoundUnaryExpr { 7 | pub: 8 | // general bound node 9 | kind BoundNodeKind = .unary_expr 10 | typ symbols.TypeSymbol 11 | child_nodes []BoundNode 12 | // child nodes 13 | op BoundUnaryOperator 14 | operand_expr BoundExpr 15 | } 16 | 17 | pub fn new_bound_unary_expr(op BoundUnaryOperator, operand_expr BoundExpr) BoundExpr { 18 | return BoundUnaryExpr{ 19 | typ: op.res_typ 20 | op: op 21 | operand_expr: operand_expr 22 | child_nodes: [BoundNode(operand_expr)] 23 | } 24 | } 25 | 26 | pub fn (ex BoundUnaryExpr) node_str() string { 27 | return typeof(ex).name 28 | } 29 | 30 | pub fn (ex BoundUnaryExpr) str() string { 31 | return '${token.token_str[ex.op.kind]}$ex.operand_expr' 32 | } 33 | 34 | pub fn (ex BoundUnaryExpr) to_ref_type() BoundUnaryExpr { 35 | return BoundUnaryExpr{ 36 | ...ex 37 | typ: ex.typ.to_ref_type() 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /lib/comp/binding/expr_var.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | import lib.comp.token 5 | 6 | pub struct BoundVariableExpr { 7 | pub: 8 | // general bound node 9 | kind BoundNodeKind = .variable_expr 10 | typ symbols.TypeSymbol 11 | child_nodes []BoundNode 12 | // child nodes 13 | names []token.Token 14 | var symbols.VariableSymbol 15 | } 16 | 17 | pub fn new_bound_variable_expr(var symbols.VariableSymbol, typ symbols.TypeSymbol) BoundExpr { 18 | return BoundVariableExpr{ 19 | var: var 20 | typ: typ 21 | } 22 | } 23 | 24 | pub fn new_bound_variable_with_names_expr(var symbols.VariableSymbol, names []token.Token, typ symbols.TypeSymbol) BoundExpr { 25 | return BoundVariableExpr{ 26 | var: var 27 | names: names 28 | typ: typ 29 | } 30 | } 31 | 32 | pub fn (ex BoundVariableExpr) node_str() string { 33 | return typeof(ex).name 34 | } 35 | 36 | pub fn (ex BoundVariableExpr) str() string { 37 | return '$ex.var.name' 38 | } 39 | 40 | pub fn (ex BoundVariableExpr) to_ref_type() BoundVariableExpr { 41 | return BoundVariableExpr{ 42 | ...ex 43 | typ: ex.typ.to_ref_type() 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /lib/comp/binding/gl_scope_stack.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | struct BoundGlobalScopeStack { 4 | mut: 5 | size int 6 | elements []&BoundGlobalScope 7 | } 8 | 9 | pub fn new_bound_global_scope_stack() BoundGlobalScopeStack { 10 | return BoundGlobalScopeStack{} 11 | } 12 | 13 | [inline] 14 | pub fn (gs BoundGlobalScopeStack) is_empty() bool { 15 | return gs.size <= 0 16 | } 17 | 18 | pub fn (gs BoundGlobalScopeStack) peek() ?&BoundGlobalScope { 19 | if !gs.is_empty() { 20 | return gs.elements[gs.size - 1] 21 | } else { 22 | return none 23 | } 24 | } 25 | 26 | pub fn (mut gs BoundGlobalScopeStack) pop() ?&BoundGlobalScope { 27 | if !gs.is_empty() { 28 | val := gs.elements[gs.size - 1] 29 | gs.size-- 30 | return val 31 | } 32 | return none 33 | } 34 | 35 | pub fn (mut gs BoundGlobalScopeStack) push(item &BoundGlobalScope) { 36 | if gs.elements.len > gs.size { 37 | gs.elements[gs.size] = item 38 | } else { 39 | gs.elements << item 40 | } 41 | gs.size++ 42 | } 43 | -------------------------------------------------------------------------------- /lib/comp/binding/gl_scope_stack_test.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.util.source 4 | import lib.comp.ast 5 | import lib.comp.symbols 6 | 7 | fn test_bound_global_scope_stack_basic() { 8 | mut stack := new_bound_global_scope_stack() 9 | stmts := []BoundStmt{} 10 | 11 | scope := new_bound_global_scope(&BoundGlobalScope(0), &source.Diagnostics(0), symbols.FunctionSymbol{}, symbols.FunctionSymbol{}, []symbols.FunctionSymbol{}, 12 | []ast.FnDeclNode{}, []symbols.VariableSymbol{}, stmts, map[string]symbols.TypeSymbol{}) 13 | 14 | stack.push(scope) 15 | 16 | assert stack.is_empty() == false 17 | 18 | pop_val := stack.pop() or { &BoundGlobalScope(0) } 19 | 20 | assert pop_val != 0 21 | } 22 | -------------------------------------------------------------------------------- /lib/comp/binding/lowerer_factory.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.token 4 | import lib.comp.symbols 5 | 6 | pub fn block(stmts ...BoundStmt) BoundStmt { 7 | return new_bound_block_stmt(stmts) 8 | } 9 | 10 | pub fn goto_cond(expr BoundExpr, true_label string, false_label string) BoundStmt { 11 | return new_bound_cond_goto_stmt(expr, true_label, false_label) 12 | } 13 | 14 | pub fn goto_label(label string) BoundStmt { 15 | return new_bound_goto_stmt(label) 16 | } 17 | 18 | pub fn label(label string) BoundStmt { 19 | return new_bound_label_stmt(label) 20 | } 21 | 22 | pub fn var_decl(var symbols.VariableSymbol, expr BoundExpr, is_mut bool) BoundVarDeclStmt { 23 | // new_local_variable_symbol(name string, typ symbols.TypeSymbol, is_mut bool) 24 | // new_local_variable_symbol 25 | return new_var_decl_stmt(var, expr, is_mut) as BoundVarDeclStmt 26 | } 27 | 28 | pub fn var_decl_local(name string, typ symbols.TypeSymbol, expr BoundExpr, is_mut bool) BoundVarDeclStmt { 29 | var := symbols.new_local_variable_symbol('local', name, typ, is_mut) 30 | return new_var_decl_stmt(var, expr, is_mut) as BoundVarDeclStmt 31 | } 32 | 33 | // new_for_stmt(cond_expr BoundExpr, body_stmt BoundStmt, has_cond bool) BoundStmt 34 | pub fn for_stmt(cond_expr BoundExpr, body_stmt BoundStmt) BoundStmt { 35 | return new_for_stmt(cond_expr, body_stmt, true) 36 | } 37 | 38 | pub fn variable_exp(var BoundVariableExpr) BoundVariableExpr { 39 | return new_bound_variable_with_names_expr(var.var, var.names, var.typ) as BoundVariableExpr 40 | } 41 | 42 | pub fn variable(var_decl BoundVarDeclStmt) BoundVariableExpr { 43 | return new_bound_variable_expr(var_decl.var, var_decl.var.typ) as BoundVariableExpr 44 | } 45 | 46 | pub fn binary(left BoundExpr, kind token.Kind, right BoundExpr) BoundExpr { 47 | // todo: fix error handling 48 | op := bind_binary_operator(kind, left.typ, right.typ) or { panic(err.msg) } 49 | 50 | return new_bound_binary_expr(left, op, right) 51 | } 52 | 53 | pub fn less_than(left BoundExpr, right BoundExpr) BoundExpr { 54 | return binary(left, .lt, right) 55 | } 56 | 57 | pub fn add(left BoundExpr, right BoundExpr) BoundExpr { 58 | return binary(left, .plus, right) 59 | } 60 | 61 | pub fn literal(val symbols.LitVal) BoundExpr { 62 | return new_bound_literal_expr(val) 63 | } 64 | 65 | pub fn increment(var_expr BoundVariableExpr) BoundStmt { 66 | incr := add(var_expr, literal(1)) 67 | incr_assign := new_bound_assign_expr(var_expr.var, incr) 68 | return new_bound_expr_stmt(incr_assign) 69 | } 70 | 71 | pub fn @for(cond_expr BoundExpr, body_stmt BoundStmt, has_cond bool) BoundStmt { 72 | return new_for_stmt(cond_expr, body_stmt, has_cond ) 73 | } -------------------------------------------------------------------------------- /lib/comp/binding/modules.md: -------------------------------------------------------------------------------- 1 | # Module design 2 | 3 | ## Import statement 4 | ### Single name 5 | When import has a single name: 6 | ```V 7 | import module_name 8 | ``` 9 | it can be ether a built-in module or a module. Search order: 10 | - built-in modules directory 11 | - file current directory 12 | - check if any match by traversing the folders until stop 13 | 14 | ## Traverse the folders 15 | 16 | - imports are converted to paths: import net.http -> net/http 17 | - the folder of file that has import as base 18 | - traverse up in folder structure until find v.mod file 19 | - if v.mod found or stop (.git dir or too many levels) 20 | if v.mod found mark all paths with the root 21 | else if v.mod not found, mark alla path with module 22 | -------------------------------------------------------------------------------- /lib/comp/binding/program.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.util.source 4 | import lib.comp.symbols 5 | 6 | [heap] 7 | pub struct BoundProgram { 8 | pub: 9 | func_bodies map[string]BoundBlockStmt 10 | func_symbols []symbols.FunctionSymbol 11 | types map[string]symbols.TypeSymbol 12 | 13 | previous &BoundProgram 14 | main_func symbols.FunctionSymbol 15 | script_func symbols.FunctionSymbol 16 | pub mut: 17 | log &source.Diagnostics 18 | } 19 | 20 | pub fn new_bound_program(previous &BoundProgram, log &source.Diagnostics, main_func symbols.FunctionSymbol, script_func symbols.FunctionSymbol, func_bodies map[string]BoundBlockStmt, func_symbols []symbols.FunctionSymbol, types map[string]symbols.TypeSymbol) &BoundProgram { 21 | return &BoundProgram{ 22 | previous: previous 23 | log: log 24 | main_func: main_func 25 | script_func: script_func 26 | func_bodies: func_bodies 27 | func_symbols: func_symbols 28 | types: types 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /lib/comp/binding/scope_test.v: -------------------------------------------------------------------------------- 1 | import lib.comp.binding 2 | import lib.comp.symbols 3 | 4 | fn test_single_scope() { 5 | mut scope := binding.new_bound_scope(&binding.BoundScope(0)) 6 | 7 | var := symbols.new_local_variable_symbol('main', 'in_scope_var', symbols.int_symbol, false) 8 | assert scope.try_declare_var(var) == true 9 | 10 | // again should result in false 11 | var_another := symbols.new_local_variable_symbol('main', 'in_scope_var', symbols.int_symbol, 12 | false) 13 | assert scope.try_declare_var(var_another) == false 14 | 15 | lookup_var := scope.lookup_var('in_scope_var') or { 16 | assert false 17 | return 18 | } 19 | assert lookup_var.name == 'in_scope_var' 20 | _ := scope.lookup_var('not_exist') or { return } 21 | 22 | assert false 23 | } 24 | 25 | fn test_parent_scope() { 26 | mut parent_scope := binding.new_bound_scope(&binding.BoundScope(0)) 27 | mut scope := binding.new_bound_scope(parent_scope) 28 | 29 | var := symbols.new_local_variable_symbol('main', 'in_scope_var', symbols.int_symbol, false) 30 | assert parent_scope.try_declare_var(var) == true 31 | 32 | // again should result in false 33 | var_another := symbols.new_local_variable_symbol('main', 'in_scope_var', symbols.int_symbol, 34 | false) 35 | assert parent_scope.try_declare_var(var_another) == false 36 | 37 | lookup_var := scope.lookup_var('in_scope_var') or { 38 | assert false 39 | return 40 | } 41 | assert lookup_var.name == 'in_scope_var' 42 | _ := scope.lookup_var('not_exist') or { return } 43 | 44 | assert false 45 | } 46 | -------------------------------------------------------------------------------- /lib/comp/binding/stack_bound_stmt.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | struct BoundStmtStack { 4 | mut: 5 | size int 6 | elements []BoundStmt 7 | } 8 | 9 | pub fn new_stack() BoundStmtStack { 10 | return BoundStmtStack{} 11 | } 12 | 13 | [inline] 14 | pub fn (stack BoundStmtStack) is_empty() bool { 15 | return stack.size <= 0 16 | } 17 | 18 | pub fn (stack BoundStmtStack) peek() ?BoundStmt { 19 | if !stack.is_empty() { 20 | return stack.elements[stack.size - 1] 21 | } else { 22 | return none 23 | } 24 | } 25 | 26 | pub fn (mut stack BoundStmtStack) pop() ?BoundStmt { 27 | if !stack.is_empty() { 28 | val := stack.elements[stack.size - 1] 29 | stack.size-- 30 | return val 31 | } 32 | return none 33 | } 34 | 35 | pub fn (mut stack BoundStmtStack) push(item BoundStmt) { 36 | if stack.elements.len > stack.size { 37 | stack.elements[stack.size] = item 38 | } else { 39 | stack.elements << item 40 | } 41 | stack.size++ 42 | } 43 | -------------------------------------------------------------------------------- /lib/comp/binding/stack_brk_cont_lbls.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | struct BreakAndContinueLabels { 4 | pub: 5 | break_label string 6 | continue_label string 7 | } 8 | 9 | fn new_break_and_cont_labels(break_label string, continue_label string) BreakAndContinueLabels { 10 | return BreakAndContinueLabels{ 11 | break_label: break_label 12 | continue_label: continue_label 13 | } 14 | } 15 | 16 | struct BreakAndContinueLabelStack { 17 | mut: 18 | size int 19 | elements []BreakAndContinueLabels 20 | } 21 | 22 | pub fn new_break_and_continue_stack() BreakAndContinueLabelStack { 23 | return BreakAndContinueLabelStack{} 24 | } 25 | 26 | [inline] 27 | pub fn (stack BreakAndContinueLabelStack) is_empty() bool { 28 | return stack.size <= 0 29 | } 30 | 31 | [inline] 32 | pub fn (stack BreakAndContinueLabelStack) len() int { 33 | return stack.elements.len 34 | } 35 | 36 | pub fn (stack BreakAndContinueLabelStack) peek() ?BreakAndContinueLabels { 37 | if !stack.is_empty() { 38 | return stack.elements[stack.size - 1] 39 | } else { 40 | return none 41 | } 42 | } 43 | 44 | pub fn (mut stack BreakAndContinueLabelStack) pop() ?BreakAndContinueLabels { 45 | if !stack.is_empty() { 46 | val := stack.elements[stack.size - 1] 47 | stack.size-- 48 | return val 49 | } 50 | return none 51 | } 52 | 53 | pub fn (mut stack BreakAndContinueLabelStack) push(item BreakAndContinueLabels) { 54 | if stack.elements.len > stack.size { 55 | stack.elements[stack.size] = item 56 | } else { 57 | stack.elements << item 58 | } 59 | stack.size++ 60 | } 61 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_assert.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.util.source 4 | 5 | pub struct BoundAssertStmt { 6 | pub: 7 | location source.TextLocation 8 | // general bound stmt 9 | kind BoundNodeKind = .assert_stmt 10 | child_nodes []BoundNode 11 | // child nodes 12 | expr BoundExpr 13 | code string 14 | } 15 | 16 | pub fn new_bound_assert_stmt(location source.TextLocation, expr BoundExpr, code string) BoundStmt { 17 | return BoundAssertStmt{ 18 | location: location 19 | expr: expr 20 | code: code 21 | } 22 | } 23 | 24 | pub fn (ex BoundAssertStmt) node_str() string { 25 | return typeof(ex).name 26 | } 27 | 28 | pub fn (ex BoundAssertStmt) str() string { 29 | return 'assert $ex.expr.str()' 30 | } 31 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_block.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import strings 4 | 5 | pub struct BoundBlockStmt { 6 | pub: 7 | // general bound stmt 8 | kind BoundNodeKind = .block_stmt 9 | child_nodes []BoundNode 10 | // child nodes 11 | stmts []BoundStmt 12 | } 13 | 14 | pub fn new_bound_block_stmt(stmts []BoundStmt) BoundBlockStmt { 15 | return BoundBlockStmt{ 16 | stmts: stmts 17 | child_nodes: stmts.map(BoundNode(it)) 18 | } 19 | } 20 | 21 | pub fn new_empty_block_stmt() BoundBlockStmt { 22 | return BoundBlockStmt{} 23 | } 24 | 25 | pub fn (ex BoundBlockStmt) node_str() string { 26 | return typeof(ex).name 27 | } 28 | 29 | pub fn (ex BoundBlockStmt) str() string { 30 | mut b := strings.new_builder(0) 31 | b.writeln('{') 32 | for stmt in ex.stmts { 33 | b.writeln('\t$stmt') 34 | } 35 | b.writeln('}') 36 | return b.str() 37 | } 38 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_break.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | pub struct BoundBreakStmt { 4 | pub: 5 | // general bound node 6 | kind BoundNodeKind = .break_stmt 7 | child_nodes []BoundNode 8 | } 9 | 10 | pub fn new_bound_break_stmt() BoundStmt { 11 | return BoundBreakStmt{} 12 | } 13 | 14 | pub fn (ex BoundBreakStmt) node_str() string { 15 | return typeof(ex).name 16 | } 17 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_comment.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.token 4 | 5 | pub struct BoundCommentStmt { 6 | pub: 7 | // general bound stmt 8 | kind BoundNodeKind = .comment_stmt 9 | child_nodes []BoundNode 10 | // child nodes 11 | comment string 12 | } 13 | 14 | pub fn new_bound_comment_stmt(comment_tok token.Token) BoundStmt { 15 | return BoundCommentStmt{ 16 | comment: comment_tok.lit 17 | } 18 | } 19 | 20 | pub fn (ex BoundCommentStmt) node_str() string { 21 | return typeof(ex).name 22 | } 23 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_cond_goto.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | pub struct BoundCondGotoStmt { 4 | pub: 5 | // general bound stmt 6 | kind BoundNodeKind = .cond_goto_stmt 7 | child_nodes []BoundNode 8 | // child nodes 9 | cond_expr BoundExpr 10 | true_label string 11 | false_label string 12 | } 13 | 14 | pub fn new_bound_cond_goto_stmt(cond_expr BoundExpr, true_label string, false_label string) BoundStmt { 15 | return BoundCondGotoStmt{ 16 | cond_expr: cond_expr 17 | true_label: true_label 18 | false_label: false_label 19 | child_nodes: [BoundNode(cond_expr)] 20 | } 21 | } 22 | 23 | pub fn (ex BoundCondGotoStmt) node_str() string { 24 | return typeof(ex).name 25 | } 26 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_continue.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | pub struct BoundContinueStmt { 4 | pub: 5 | // general bound stmt 6 | kind BoundNodeKind = .continue_stmt 7 | child_nodes []BoundNode 8 | } 9 | 10 | pub fn new_bound_continue_stmt() BoundStmt { 11 | return BoundContinueStmt{} 12 | } 13 | 14 | pub fn (ex BoundContinueStmt) node_str() string { 15 | return typeof(ex).name 16 | } 17 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_expr.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | pub struct BoundExprStmt { 4 | pub: 5 | // general bound stmt 6 | kind BoundNodeKind = .expr_stmt 7 | child_nodes []BoundNode 8 | // child nodes 9 | expr BoundExpr 10 | } 11 | 12 | pub fn new_bound_expr_stmt(expr BoundExpr) BoundExprStmt { 13 | return BoundExprStmt{ 14 | expr: expr 15 | child_nodes: [BoundNode(expr)] 16 | } 17 | } 18 | 19 | pub fn (ex BoundExprStmt) node_str() string { 20 | return typeof(ex).name 21 | } 22 | 23 | pub fn (ex BoundExprStmt) str() string { 24 | return '$ex.expr' 25 | } 26 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_for.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundForRangeStmt { 6 | pub: 7 | // general bound stmt 8 | kind BoundNodeKind = .for_range_stmt 9 | child_nodes []BoundNode 10 | // child nodes 11 | ident symbols.VariableSymbol 12 | range_expr BoundExpr 13 | body_stmt BoundStmt 14 | } 15 | 16 | fn new_for_range_stmt(ident symbols.VariableSymbol, range_expr BoundExpr, body_stmt BoundStmt) BoundStmt { 17 | return BoundForRangeStmt{ 18 | ident: ident 19 | range_expr: range_expr 20 | body_stmt: body_stmt 21 | child_nodes: [BoundNode(range_expr), body_stmt] 22 | } 23 | } 24 | 25 | pub fn (ex BoundForRangeStmt) node_str() string { 26 | return typeof(ex).name 27 | } 28 | 29 | pub fn (ex BoundForRangeStmt) str() string { 30 | return 'for $ex.ident.name in $ex.range_expr $ex.body_stmt' 31 | } 32 | 33 | pub struct BoundForStmt { 34 | pub: 35 | has_cond bool 36 | // general bound stmt 37 | kind BoundNodeKind = .for_stmt 38 | child_nodes []BoundNode 39 | // child nodes 40 | cond_expr BoundExpr 41 | body_stmt BoundStmt 42 | } 43 | 44 | pub fn new_for_stmt(cond_expr BoundExpr, body_stmt BoundStmt, has_cond bool) BoundStmt { 45 | return BoundForStmt{ 46 | cond_expr: cond_expr 47 | body_stmt: body_stmt 48 | has_cond: has_cond 49 | child_nodes: [BoundNode(cond_expr), body_stmt] 50 | } 51 | } 52 | 53 | pub fn (ex BoundForStmt) node_str() string { 54 | return typeof(ex).name 55 | } 56 | 57 | pub fn (ex BoundForStmt) str() string { 58 | if ex.has_cond { 59 | return 'for $ex.cond_expr $ex.body_stmt' 60 | } else { 61 | return 'for $ex.body_stmt' 62 | } 63 | } 64 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_goto.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | pub struct BoundGotoStmt { 4 | pub: 5 | // general bound stmt 6 | kind BoundNodeKind = .goto_stmt 7 | child_nodes []BoundNode 8 | // child nodes 9 | label string 10 | } 11 | 12 | pub fn new_bound_goto_stmt(label string) BoundStmt { 13 | return BoundGotoStmt{ 14 | label: label 15 | } 16 | } 17 | 18 | pub fn (ex BoundGotoStmt) node_str() string { 19 | return typeof(ex).name 20 | } 21 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_if.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | pub struct BoundIfStmt { 4 | pub: 5 | // general bound stmt 6 | kind BoundNodeKind = .if_stmt 7 | child_nodes []BoundNode 8 | // child nodes 9 | cond_expr BoundExpr 10 | has_else bool 11 | then_stmt BoundStmt 12 | else_stmt BoundStmt 13 | } 14 | 15 | fn new_if_stmt(cond_expr BoundExpr, then_stmt BoundStmt) BoundStmt { 16 | return BoundIfStmt{ 17 | cond_expr: cond_expr 18 | // typ: cond_expr.typ 19 | then_stmt: then_stmt 20 | child_nodes: [BoundNode(cond_expr), then_stmt] 21 | } 22 | } 23 | 24 | fn new_if_else_stmt(cond_expr BoundExpr, then_stmt BoundStmt, else_stmt BoundStmt) BoundStmt { 25 | return BoundIfStmt{ 26 | cond_expr: cond_expr 27 | then_stmt: then_stmt 28 | else_stmt: else_stmt 29 | has_else: true 30 | child_nodes: [BoundNode(cond_expr), then_stmt, else_stmt] 31 | } 32 | } 33 | 34 | pub fn (ex BoundIfStmt) node_str() string { 35 | return typeof(ex).name 36 | } 37 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_import.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.token 4 | 5 | pub struct BoundImportStmt { 6 | pub: 7 | // general bound stmt 8 | kind BoundNodeKind = .import_stmt 9 | child_nodes []BoundNode 10 | // child nodes 11 | name string 12 | } 13 | 14 | pub fn new_bound_import_stmt(tok_name token.Token) BoundStmt { 15 | return BoundImportStmt{ 16 | name: tok_name.lit 17 | } 18 | } 19 | 20 | pub fn (ex BoundImportStmt) node_str() string { 21 | return typeof(ex).name 22 | } 23 | 24 | pub fn (ex BoundImportStmt) str() string { 25 | return 'import $ex.name' 26 | } 27 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_label.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | pub struct BoundLabelStmt { 4 | pub: 5 | // general bound stmt 6 | kind BoundNodeKind = .label_stmt 7 | child_nodes []BoundNode 8 | // child nodes 9 | name string 10 | } 11 | 12 | pub fn new_bound_label_stmt(name string) BoundStmt { 13 | return BoundLabelStmt{ 14 | name: name 15 | } 16 | } 17 | 18 | pub fn (ex BoundLabelStmt) node_str() string { 19 | return typeof(ex).name 20 | } 21 | 22 | pub fn (ex BoundLabelStmt) str() string { 23 | return '$ex.name:' 24 | } 25 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_module.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.token 4 | 5 | pub struct BoundModuleStmt { 6 | pub: 7 | // general bound stmt 8 | kind BoundNodeKind = .module_stmt 9 | child_nodes []BoundNode 10 | // child nodes 11 | name string 12 | } 13 | 14 | pub fn new_bound_module_stmt(tok_name token.Token) BoundStmt { 15 | return BoundModuleStmt{ 16 | name: tok_name.lit 17 | } 18 | } 19 | 20 | pub fn (ex BoundModuleStmt) node_str() string { 21 | return typeof(ex).name 22 | } 23 | 24 | pub fn (ex BoundModuleStmt) str() string { 25 | return 'module $ex.name' 26 | } 27 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_return.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | pub struct BoundReturnStmt { 4 | pub: 5 | has_expr bool 6 | // general bound stmt 7 | kind BoundNodeKind = .return_stmt 8 | child_nodes []BoundNode 9 | // child nodes 10 | expr BoundExpr 11 | } 12 | 13 | pub fn new_bound_return_with_expr_stmt(expr BoundExpr) BoundStmt { 14 | return BoundReturnStmt{ 15 | expr: expr 16 | has_expr: true 17 | child_nodes: [BoundNode(expr)] 18 | } 19 | } 20 | 21 | pub fn new_bound_return_stmt() BoundStmt { 22 | return BoundReturnStmt{ 23 | has_expr: false 24 | } 25 | } 26 | 27 | pub fn (ex BoundReturnStmt) node_str() string { 28 | return typeof(ex).name 29 | } 30 | 31 | pub fn (ex BoundReturnStmt) str() string { 32 | if ex.has_expr { 33 | return 'return $ex.expr' 34 | } else { 35 | return 'return' 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /lib/comp/binding/stmt_var_decl.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | pub struct BoundVarDeclStmt { 6 | pub: 7 | is_mut bool 8 | is_ref bool 9 | // general bound stmt 10 | kind BoundNodeKind = .var_decl_stmt 11 | child_nodes []BoundNode 12 | // child nodes 13 | expr BoundExpr 14 | var symbols.VariableSymbol 15 | } 16 | 17 | pub fn new_var_decl_stmt(var symbols.VariableSymbol, expr BoundExpr, is_mut bool) BoundStmt { 18 | return BoundVarDeclStmt{ 19 | var: var 20 | is_mut: is_mut 21 | expr: expr 22 | child_nodes: [BoundNode(expr)] 23 | } 24 | } 25 | 26 | pub fn (ex BoundVarDeclStmt) node_str() string { 27 | return typeof(ex).name 28 | } 29 | 30 | pub fn (ex BoundVarDeclStmt) str() string { 31 | return '$ex.var.name := $ex.expr' 32 | } 33 | -------------------------------------------------------------------------------- /lib/comp/binding/type.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | pub type BoundExpr = BoundArrayInitExpr | BoundAssignExpr | BoundBinaryExpr | BoundCallExpr | 4 | BoundConvExpr | BoundErrorExpr | BoundIfExpr | BoundIndexExpr | BoundLiteralExpr | 5 | BoundNoneExpr | BoundRangeExpr | BoundStructInitExpr | BoundUnaryExpr | BoundVariableExpr | 6 | NoneExpr 7 | 8 | pub type BoundStmt = BoundAssertStmt | BoundBlockStmt | BoundBreakStmt | BoundCommentStmt | 9 | BoundCondGotoStmt | BoundContinueStmt | BoundExprStmt | BoundForRangeStmt | BoundForStmt | 10 | BoundGotoStmt | BoundIfStmt | BoundLabelStmt | BoundModuleStmt | BoundReturnStmt | 11 | BoundVarDeclStmt | BoundImportStmt 12 | 13 | pub type BoundNode = BoundExpr | BoundStmt 14 | 15 | pub enum BoundNodeKind { 16 | // Expr 17 | unary_expr 18 | binary_expr 19 | literal_expr 20 | variable_expr 21 | assign_expr 22 | if_expr 23 | range_expr 24 | call_expr 25 | conv_expr 26 | error_expr 27 | sruct_init_expr 28 | index_expr 29 | none_expr 30 | // Stmts 31 | block_stmt 32 | expr_stmt 33 | var_decl_stmt 34 | if_stmt 35 | for_stmt 36 | label_stmt 37 | break_stmt 38 | continue_stmt 39 | return_stmt 40 | for_range_stmt 41 | cond_goto_stmt 42 | goto_stmt 43 | comment_stmt 44 | module_stmt 45 | import_stmt 46 | assert_stmt 47 | } 48 | 49 | pub fn (bn &BoundNode) child_nodes() []BoundNode { 50 | return bn.child_nodes 51 | } 52 | 53 | pub fn (bn BoundNode) node_str() string { 54 | match bn { 55 | BoundExpr { 56 | return bn.node_str() 57 | } 58 | BoundStmt { 59 | return bn.node_str() 60 | } 61 | } 62 | } 63 | 64 | pub fn (be BoundExpr) typ_str() string { 65 | return be.typ.name 66 | } 67 | 68 | pub fn (be BoundExpr) node_str() string { 69 | match be { 70 | BoundLiteralExpr { return be.node_str() } 71 | BoundUnaryExpr { return be.node_str() } 72 | BoundBinaryExpr { return be.node_str() } 73 | BoundVariableExpr { return be.node_str() } 74 | BoundAssignExpr { return be.node_str() } 75 | BoundIfExpr { return be.node_str() } 76 | BoundRangeExpr { return be.node_str() } 77 | BoundIndexExpr { return be.node_str() } 78 | BoundErrorExpr { return be.node_str() } 79 | BoundCallExpr { return be.node_str() } 80 | BoundConvExpr { return be.node_str() } 81 | BoundNoneExpr { return be.node_str() } 82 | BoundArrayInitExpr { return be.node_str() } 83 | BoundStructInitExpr { return be.node_str() } 84 | NoneExpr { return be.node_str() } 85 | } 86 | } 87 | 88 | pub fn (be BoundExpr) str() string { 89 | match be { 90 | BoundLiteralExpr { return be.str() } 91 | BoundUnaryExpr { return be.str() } 92 | BoundBinaryExpr { return be.str() } 93 | BoundVariableExpr { return be.str() } 94 | BoundAssignExpr { return be.str() } 95 | BoundIfExpr { return be.str() } 96 | BoundRangeExpr { return be.str() } 97 | BoundIndexExpr { return be.str() } 98 | BoundErrorExpr { return be.str() } 99 | BoundCallExpr { return be.str() } 100 | BoundConvExpr { return be.str() } 101 | BoundNoneExpr { return be.str() } 102 | BoundStructInitExpr { return be.str() } 103 | BoundArrayInitExpr { return be.str() } 104 | NoneExpr { return be.str() } 105 | } 106 | } 107 | 108 | pub fn (be BoundExpr) to_ref_type() BoundExpr { 109 | match be { 110 | BoundLiteralExpr { return be.to_ref_type() } 111 | BoundUnaryExpr { return be.to_ref_type() } 112 | BoundBinaryExpr { return be.to_ref_type() } 113 | BoundVariableExpr { return be.to_ref_type() } 114 | BoundAssignExpr { return be.to_ref_type() } 115 | BoundIfExpr { return be.to_ref_type() } 116 | BoundRangeExpr { return be.to_ref_type() } 117 | BoundIndexExpr { return be.to_ref_type() } 118 | BoundErrorExpr { return be.to_ref_type() } 119 | BoundCallExpr { return be.to_ref_type() } 120 | BoundConvExpr { return be.to_ref_type() } 121 | BoundNoneExpr { return be.to_ref_type() } 122 | BoundStructInitExpr { return be.to_ref_type() } 123 | BoundArrayInitExpr { return be.to_ref_type() } 124 | NoneExpr { return be.to_ref_type() } 125 | } 126 | } 127 | 128 | pub fn (be BoundExpr) kind() BoundNodeKind { 129 | return be.kind 130 | } 131 | 132 | pub fn (bs BoundStmt) node_str() string { 133 | match bs { 134 | BoundBlockStmt { return bs.node_str() } 135 | BoundExprStmt { return bs.node_str() } 136 | BoundForRangeStmt { return bs.node_str() } 137 | BoundForStmt { return bs.node_str() } 138 | BoundIfStmt { return bs.node_str() } 139 | BoundVarDeclStmt { return bs.node_str() } 140 | BoundGotoStmt { return bs.node_str() } 141 | BoundCondGotoStmt { return bs.node_str() } 142 | BoundLabelStmt { return bs.node_str() } 143 | BoundBreakStmt { return bs.node_str() } 144 | BoundContinueStmt { return bs.node_str() } 145 | BoundReturnStmt { return bs.node_str() } 146 | BoundCommentStmt { return bs.node_str() } 147 | BoundModuleStmt { return bs.node_str() } 148 | BoundImportStmt { return bs.node_str() } 149 | BoundAssertStmt { return bs.node_str() } 150 | } 151 | } 152 | 153 | pub fn (bs BoundStmt) str() string { 154 | match bs { 155 | BoundBlockStmt { return bs.str() } 156 | BoundExprStmt { return bs.str() } 157 | BoundForRangeStmt { return bs.str() } 158 | BoundForStmt { return bs.str() } 159 | BoundIfStmt { return bs.str() } 160 | BoundVarDeclStmt { return bs.str() } 161 | BoundGotoStmt { return bs.str() } 162 | BoundCondGotoStmt { return bs.str() } 163 | BoundLabelStmt { return bs.str() } 164 | BoundBreakStmt { return bs.str() } 165 | BoundContinueStmt { return bs.str() } 166 | BoundReturnStmt { return bs.str() } 167 | BoundCommentStmt { return bs.str() } 168 | BoundModuleStmt { return bs.str() } 169 | BoundImportStmt { return bs.str() } 170 | BoundAssertStmt { return bs.str() } 171 | } 172 | } 173 | -------------------------------------------------------------------------------- /lib/comp/binding/variables.v: -------------------------------------------------------------------------------- 1 | module binding 2 | 3 | import lib.comp.symbols 4 | 5 | [heap] 6 | pub struct EvalVariables { 7 | mut: 8 | vars map[string]symbols.LitVal 9 | } 10 | 11 | pub fn new_eval_variables() &EvalVariables { 12 | return &EvalVariables{ 13 | vars: map[string]symbols.LitVal{} 14 | } 15 | } 16 | 17 | pub fn (mut ev EvalVariables) assign_variable_value(var symbols.VariableSymbol, val symbols.LitVal) { 18 | ev.vars[var.id] = val 19 | } 20 | 21 | pub fn (mut ev EvalVariables) lookup(var symbols.VariableSymbol) ?symbols.LitVal { 22 | val := ev.vars[var.id] or { return none } 23 | return val 24 | } 25 | 26 | struct EvalVarsStack { 27 | mut: 28 | size int 29 | elements []&EvalVariables 30 | } 31 | 32 | pub fn new_eval_vars_stack() EvalVarsStack { 33 | return EvalVarsStack{} 34 | } 35 | 36 | [inline] 37 | pub fn (stack EvalVarsStack) is_empty() bool { 38 | return stack.size <= 0 39 | } 40 | 41 | [inline] 42 | pub fn (stack EvalVarsStack) len() int { 43 | return stack.elements.len 44 | } 45 | 46 | pub fn (stack EvalVarsStack) peek() ?&EvalVariables { 47 | if !stack.is_empty() { 48 | return stack.elements[stack.size - 1] 49 | } else { 50 | return none 51 | } 52 | } 53 | 54 | pub fn (mut stack EvalVarsStack) pop() ?&EvalVariables { 55 | if !stack.is_empty() { 56 | val := stack.elements[stack.size - 1] 57 | stack.size-- 58 | return val 59 | } 60 | return none 61 | } 62 | 63 | pub fn (mut stack EvalVarsStack) push(item &EvalVariables) { 64 | if stack.elements.len > stack.size { 65 | stack.elements[stack.size] = item 66 | } else { 67 | stack.elements << item 68 | } 69 | stack.size++ 70 | } 71 | -------------------------------------------------------------------------------- /lib/comp/binding/walker/print.v: -------------------------------------------------------------------------------- 1 | module walker 2 | 3 | import strings 4 | import term 5 | import lib.comp.binding 6 | import lib.comp.parser 7 | 8 | pub struct BoundNodePrinter { 9 | mut: 10 | tree []string 11 | } 12 | 13 | pub fn node_str(node binding.BoundNode) string { 14 | p := BoundNodePrinter{} 15 | walk_tree(p, node) 16 | mut b := strings.new_builder(0) 17 | for s in p.tree { 18 | b.write_string(s) 19 | } 20 | return b.str() 21 | } 22 | 23 | pub fn print_block(block binding.BoundBlockStmt, shallow bool) string { 24 | mut b := strings.new_builder(0) 25 | for stmt in block.child_nodes { 26 | s := node_str(stmt) 27 | b.write_string(s) 28 | } 29 | return b.str() 30 | } 31 | 32 | pub fn print_expression(expr string, shallow bool) string { 33 | // vars := binding.new_eval_variables() 34 | syntax_tree := parser.parse_syntax_tree(expr) 35 | if syntax_tree.log.all.len > 0 { 36 | return 'syntax error' 37 | } 38 | scope := binding.bind_global_scope(&binding.BoundGlobalScope(0), syntax_tree.root) 39 | lower := if !shallow { lowering.lower(scope.stmt) } else { lowering.lower_shallow(scope.stmt) } 40 | // mut comp := comp.new_compilation(syntax_tree) 41 | // res := comp.evaluate(vars) 42 | if scope.log.all.len > 0 { 43 | return 'error binding expression' 44 | } 45 | mut b := strings.new_builder(0) 46 | for stmt in lower.child_nodes { 47 | s := node_str(stmt) 48 | b.write_string(s) 49 | } 50 | return b.str() 51 | } 52 | 53 | fn (mut p BoundNodePrinter) visit_btree(node binding.BoundNode, last_child bool, indent string) ?string { 54 | mut b := strings.new_builder(0) 55 | 56 | marker := if last_child { '└──' } else { '├──' } 57 | 58 | b.write_string(term.gray(indent)) 59 | if indent.len > 0 { 60 | b.write_string(term.gray(marker)) 61 | } 62 | new_ident := indent + if last_child { ' ' } else { '│ ' } 63 | node_str := node.node_str() 64 | 65 | b.write_string(term.gray(node_str[9..])) 66 | match node { 67 | binding.BoundExpr { 68 | if node is binding.BoundLiteralExpr { 69 | b.writeln(term.bright_cyan(' $node.val')) 70 | } else if node is binding.BoundBinaryExpr { 71 | b.writeln(term.bright_cyan(' $node.op.kind')) 72 | } else if node is binding.BoundVariableExpr { 73 | b.writeln(term.bright_cyan(' $node.var.name() ($node.var.typ.name)')) 74 | } else if node is binding.BoundUnaryExpr { 75 | b.writeln(term.bright_cyan(' $node.op.kind')) 76 | } else { 77 | b.writeln('') 78 | } 79 | } 80 | binding.BoundStmt { 81 | if node is binding.BoundLabelStmt { 82 | b.writeln(term.bright_cyan(' $node.name')) 83 | } else if node is binding.BoundCondGotoStmt { 84 | b.writeln(term.bright_cyan(' $node.jump_if_true -> $node.label')) 85 | } else if node is binding.BoundGotoStmt { 86 | b.writeln(term.bright_cyan(' $node.label')) 87 | } else if node is binding.BoundForStmt { 88 | b.writeln(term.bright_cyan(' $node.child_nodes.len')) 89 | } else if node is binding.BoundVarDeclStmt { 90 | b.writeln(term.bright_cyan(' $node.var.name() ($node.var.typ.name')) 91 | } else { 92 | b.writeln('') 93 | } 94 | } 95 | } 96 | 97 | p.tree << b.str() 98 | return new_ident 99 | } 100 | -------------------------------------------------------------------------------- /lib/comp/binding/walker/walker.v: -------------------------------------------------------------------------------- 1 | module walker 2 | 3 | import lib.comp.binding 4 | 5 | // Visitor defines a visit method which is invoked by the walker in each node it encounters. 6 | pub interface Visitor { 7 | visit(node binding.BoundNode) ? 8 | } 9 | 10 | pub interface VisitorTree { 11 | visit_btree(node binding.BoundNode, last_child bool, tree_info string) ?string 12 | } 13 | 14 | pub type InspectorFn = fn (node binding.BoundNode, data voidptr) bool 15 | 16 | struct Inspector { 17 | inspector_callback InspectorFn 18 | mut: 19 | data voidptr 20 | } 21 | 22 | pub fn (i &Inspector) visit(node binding.BoundNode) ? { 23 | if i.inspector_callback(node, i.data) { 24 | return 25 | } 26 | return none 27 | } 28 | 29 | // inspect traverses and checks the AST node on a depth-first order and based on the data given 30 | pub fn inspect(node binding.BoundNode, data voidptr, inspector_callback InspectorFn) { 31 | walk(Inspector{inspector_callback, data}, node) 32 | } 33 | 34 | // walk traverses the AST using the given visitor 35 | pub fn walk(visitor Visitor, node binding.BoundNode) { 36 | visitor.visit(node) or { return } 37 | children := node.child_nodes() 38 | for child_node in children { 39 | walk(visitor, &child_node) 40 | } 41 | } 42 | 43 | fn walk_tree_recursive(visitor VisitorTree, node binding.BoundNode, last_child bool, tree_info string) { 44 | t_info := visitor.visit_btree(node, last_child, tree_info) or { return } 45 | children := node.child_nodes() 46 | for i, child_node in children { 47 | walk_tree_recursive(visitor, &child_node, i == children.len - 1, t_info) 48 | } 49 | } 50 | 51 | pub fn walk_tree(visitor VisitorTree, node binding.BoundNode) { 52 | walk_tree_recursive(visitor, node, true, '') 53 | } 54 | -------------------------------------------------------------------------------- /lib/comp/gen/gen.v: -------------------------------------------------------------------------------- 1 | module gen 2 | 3 | import lib.comp.util.source 4 | import lib.comp.util.pref 5 | import lib.comp.binding 6 | 7 | pub interface Generator { 8 | generate(pref pref.CompPref, program &binding.BoundProgram) &source.Diagnostics 9 | run(program &binding.BoundProgram, pref pref.CompPref) &source.Diagnostics 10 | run_tests(program &binding.BoundProgram, pref pref.CompPref) &source.Diagnostics 11 | } 12 | -------------------------------------------------------------------------------- /lib/comp/gen/golang/gen_fns.v: -------------------------------------------------------------------------------- 1 | module golang 2 | 3 | import lib.comp.io 4 | import lib.comp.symbols 5 | 6 | pub fn write_symbol(writer io.CodeWriter, symbol symbols.Symbol) { 7 | match symbol { 8 | symbols.VariableSymbol { 9 | write_var_symbol(writer, symbol) 10 | } 11 | // BoundStmt { write_stmt(writer, node) } 12 | symbols.FunctionSymbol { 13 | write_function_symbol(writer, symbol) 14 | } 15 | symbols.TypeSymbol { 16 | write_type_symbol(writer, symbol) 17 | } 18 | symbols.ConstSymbol { 19 | write_const_symbol(writer, symbol) 20 | } 21 | } 22 | } 23 | 24 | fn write_param_symbol(writer io.CodeWriter, param_symbol symbols.ParamSymbol) { 25 | // if param_symbol.is_mut { 26 | // writer.write('mut') 27 | // writer.write_space() 28 | // } 29 | writer.write(param_symbol.name) 30 | writer.write_space() 31 | write_type_symbol(writer, param_symbol.typ) 32 | } 33 | 34 | fn write_function_symbol(writer io.CodeWriter, fn_symbol symbols.FunctionSymbol) { 35 | writer.write('func') 36 | writer.write_space() 37 | writer.write(fn_symbol.name) 38 | writer.write('(') 39 | for i, param in fn_symbol.params { 40 | if i != 0 { 41 | writer.write(',') 42 | writer.write_space() 43 | } 44 | write_param_symbol(writer, param) 45 | } 46 | writer.write(')') 47 | if fn_symbol.typ != symbols.void_symbol { 48 | writer.write_space() 49 | write_type_symbol(writer, fn_symbol.typ) 50 | } 51 | } 52 | 53 | fn write_type_symbol(writer io.CodeWriter, type_symbol symbols.TypeSymbol) { 54 | writer.write(type_symbol.name) 55 | } 56 | 57 | fn write_const_symbol(writer io.CodeWriter, const_symbol symbols.ConstSymbol) { 58 | writer.write(const_symbol.val.str()) 59 | } 60 | 61 | fn write_var_symbol(writer io.CodeWriter, var_symbol symbols.VariableSymbol) { 62 | match var_symbol { 63 | symbols.LocalVariableSymbol {} 64 | symbols.GlobalVariableSymbol {} 65 | symbols.ParamSymbol { 66 | write_param_symbol(writer, var_symbol) 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /lib/comp/gen/golang/go_gen.v: -------------------------------------------------------------------------------- 1 | module golang 2 | 3 | import os 4 | import lib.comp.util.source 5 | import lib.comp.util.pref 6 | import lib.comp.binding 7 | import lib.comp.io 8 | 9 | struct GolangGen { 10 | mut: 11 | log &source.Diagnostics 12 | program &binding.BoundProgram = 0 13 | binary_full_path string 14 | gofile_full_path string 15 | } 16 | 17 | pub fn new_golang_generator() GolangGen { 18 | return GolangGen{ 19 | log: source.new_diagonistics() 20 | } 21 | } 22 | 23 | pub fn (mut g GolangGen) run(program &binding.BoundProgram, pref pref.CompPref) &source.Diagnostics { 24 | return log 25 | } 26 | 27 | pub fn (mut g GolangGen) run_tests(program &binding.BoundProgram) &source.Diagnostics { 28 | return log 29 | } 30 | 31 | pub fn (mut g GolangGen) generate(pref pref.CompPref, program &binding.BoundProgram) &source.Diagnostics { 32 | g.program = program 33 | g.binary_full_path = pref.output 34 | 35 | binary_directory := os.dir(pref.output) 36 | binary_name := os.file_name(pref.output) 37 | g.gofile_full_path = os.join_path(binary_directory, '${binary_name}.go') 38 | 39 | // Generate code 40 | g.generate_code() 41 | 42 | if g.log.all.len > 0 { 43 | return g.log 44 | } 45 | // Compile generated code 46 | g.compile_code() 47 | return g.log 48 | } 49 | 50 | fn (mut g GolangGen) generate_code() { 51 | mut cw := io.new_general_code_writer() 52 | 53 | main_template := os.read_file('lib/comp/gen/golang/templates/main.go') or { 54 | g.log.error_msg(err.msg) 55 | return 56 | } 57 | cw.writeln(main_template) 58 | 59 | for func in g.program.func_symbols { 60 | if func.name != 'main' { 61 | body := g.program.func_bodies[func.id] 62 | write_symbol(cw, func) 63 | cw.write_space() 64 | write_node(cw, binding.BoundStmt(body)) 65 | } 66 | } 67 | 68 | // write main func 69 | main_func := g.program.main_func 70 | main_body := g.program.func_bodies[main_func.id] 71 | write_symbol(cw, main_func) 72 | cw.write_space() 73 | write_node(cw, binding.BoundStmt(main_body)) 74 | 75 | // write code to file 76 | 77 | os.write_file(g.gofile_full_path, cw.str()) or { g.log.error_msg(err.msg) } 78 | } 79 | 80 | fn (mut g GolangGen) compile_code() { 81 | compile_command := 'go build -o $g.binary_full_path $g.gofile_full_path' 82 | println('COMPILE COMMAND: $compile_command') 83 | res := os.execute(compile_command) 84 | if res.exit_code != 0 { 85 | g.log.error_msg(res.output) 86 | } 87 | } 88 | -------------------------------------------------------------------------------- /lib/comp/gen/golang/lowerer_go.v: -------------------------------------------------------------------------------- 1 | module golang 2 | 3 | import lib.comp.binding as bi 4 | import lib.comp.symbols 5 | 6 | // The for range is transformed it two stages, this stage transforms it 7 | // to a normal for statement and then to a golan while statement in code gen 8 | 9 | // for in .. 10 | // 11 | // 12 | // -----> 13 | // { 14 | // mut index := 15 | // upper := 16 | // for index < upper { 17 | // 18 | // index = index + 1 19 | // } 20 | // } 21 | 22 | fn lower_for_range(for_range_stmt bi.BoundForRangeStmt) []bi.BoundStmt { 23 | mut stmts := []bi.BoundStmt{} 24 | 25 | range := for_range_stmt.range_expr as bi.BoundRangeExpr 26 | 27 | // mut index := lower 28 | index_decl := bi.var_decl(for_range_stmt.ident, range.from_exp, true) 29 | // upper := upper 30 | upper_decl := bi.var_decl_local('upper', symbols.int_symbol, range.to_exp, false) 31 | 32 | stmts << index_decl 33 | stmts << upper_decl 34 | 35 | // add a ending statement that will increase the index 36 | for_body := for_range_stmt.body_stmt as bi.BoundBlockStmt 37 | mut body_stmts := []bi.BoundStmt{cap: for_body.stmts.len + 1} 38 | 39 | body_stmts << for_body.stmts 40 | // i = i + 1 41 | body_stmts << bi.increment(bi.variable(index_decl)) 42 | 43 | // for_range_stmt.body_stmt 44 | for_stmt := bi.@for(bi.less_than(bi.variable(index_decl), bi.variable(upper_decl)), 45 | bi.new_bound_block_stmt(body_stmts), true) 46 | 47 | stmts << for_stmt 48 | return stmts 49 | } 50 | -------------------------------------------------------------------------------- /lib/comp/gen/golang/templates/main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strconv" 6 | ) 7 | 8 | // built-in functions 9 | func print(a ...interface{}) { 10 | fmt.Print(a...) 11 | } 12 | 13 | func println(a ...interface{}) { 14 | fmt.Println(a...) 15 | } 16 | 17 | // conversion functions 18 | func i_to_s(i int) string { 19 | return strconv.Itoa(i) 20 | } 21 | 22 | // convenience functions 23 | func if_then_else(condition bool, a interface{}, b interface{}) interface{} { 24 | if condition { 25 | return a 26 | } 27 | return b 28 | } 29 | 30 | // Generated code 31 | -------------------------------------------------------------------------------- /lib/comp/gen/llvm/emit/emit_fn_call.v: -------------------------------------------------------------------------------- 1 | module emit 2 | 3 | import lib.comp.gen.llvm.core 4 | import lib.comp.symbols 5 | import lib.comp.binding 6 | 7 | fn (mut fd FunctionDecl) emit_call_builtin(name string, args ...core.Value) core.Value { 8 | func_name := if !name.starts_with('C.') { name } else { name[2..] } 9 | res := fd.em.funcs.filter(it.name == func_name) 10 | if res.len == 0 { 11 | panic('builtin function $name not declared in $fd.em.built_in_funcs') 12 | } 13 | func := res[0] 14 | return fd.bld.create_call2(func.typ, func.val, args) 15 | } 16 | 17 | fn (mut fd FunctionDecl) emit_variable_value(var &symbols.VariableSymbol, expr &binding.BoundExpr, val core.Value) core.Value { 18 | if var.is_ref && val.is_constant() { 19 | var_typ := fd.em.get_type_from_type_symb(var.typ) 20 | return fd.bld.alloca_and_store(var_typ, val, '') 21 | } 22 | mut expr_is_ref := expr.typ.is_ref 23 | if !var.is_ref { 24 | if expr is binding.BoundVariableExpr { 25 | expr_is_ref = expr_is_ref || expr.var.is_ref 26 | } 27 | if expr_is_ref { 28 | var_typ := fd.em.get_type_from_type_symb(var.typ) 29 | return fd.bld.create_load2(var_typ, val) 30 | } 31 | } 32 | return val 33 | } 34 | 35 | fn (mut fd FunctionDecl) emit_call_fn(call_expr binding.BoundCallExpr) core.Value { 36 | func_res := fd.em.funcs.filter(it.func > 0 && it.func.id == call_expr.func.id) 37 | if func_res.len != 1 { 38 | panic('unexpected, function $call_expr.func.name not declared. ($fd.em.funcs.len)') 39 | } 40 | func_decl := func_res[0] 41 | args_len := if func_decl.func.receiver.is_empty { 42 | call_expr.params.len 43 | } else { 44 | call_expr.params.len + 1 45 | } 46 | mut args := []core.Value{cap: args_len} 47 | if !func_decl.func.receiver.is_empty { 48 | rec_var := fd.em.var_decl[call_expr.receiver.id] or { 49 | panic('receiver: $call_expr.receiver ($call_expr.receiver.id) is not declared $fd.em.var_decl, $fd.em.var_decl.len') 50 | } 51 | if !func_decl.func.receiver.is_ref { 52 | args << fd.dereference(rec_var) 53 | } else { 54 | args << rec_var // c.handle_box_unbox_variable(func.func.receiver, rec_var) 55 | } 56 | } 57 | for i, param_expr in call_expr.params { 58 | expr_val := fd.emit_expr(param_expr) 59 | decl_param := func_decl.func.params[i] 60 | args << fd.emit_variable_value(&decl_param, ¶m_expr, expr_val) 61 | } 62 | 63 | if call_expr.typ.kind == symbols.TypeSymbolKind.void_symbol { 64 | // no return value 65 | fd.bld.create_call2(func_decl.typ, func_decl.val, args) 66 | return core.Value{ 67 | c: 0 68 | } 69 | } 70 | return fd.bld.create_call2(func_decl.typ, func_decl.val, args) 71 | } 72 | -------------------------------------------------------------------------------- /lib/comp/gen/llvm/emit/emit_globals.v: -------------------------------------------------------------------------------- 1 | module emit 2 | 3 | import lib.comp.gen.llvm.core 4 | 5 | const ( 6 | no_name = '\00' 7 | ) 8 | 9 | pub enum GlobalVarRefType { 10 | jmp_buff 11 | printf_str 12 | printf_str_nl 13 | printf_num 14 | sprintf_buff 15 | str_true 16 | str_false 17 | nl 18 | } 19 | 20 | pub fn (mut em EmitModule) emit_global_vars() { 21 | // add the global sprintf buffer 22 | buff_typ := em.ctx.int8_type().to_array_type(21) 23 | buff_val := em.mod.add_global('sprintf_buff', buff_typ) 24 | 25 | null_val := buff_typ.const_null() 26 | buff_val.set_initializer(null_val) 27 | em.global_const[GlobalVarRefType.sprintf_buff] = buff_val 28 | 29 | jmp_buf_typ_ref := em.types['C.JumpBuffer'] or { 30 | panic('type `C.JumpBuffer` is not in $em.types.keys() $') 31 | } 32 | mut values := [core.const_int(em.ctx.int64_type(), u64(0), false)] 33 | init_struct := jmp_buf_typ_ref.create_const_named_struct(values) 34 | global_jmp_buff_val := em.mod.add_global('jmp_buf', jmp_buf_typ_ref) 35 | global_jmp_buff_val.set_initializer(init_struct) 36 | em.global_const[GlobalVarRefType.jmp_buff] = global_jmp_buff_val 37 | } 38 | 39 | fn (mut em EmitModule) get_global_string(glob_typ GlobalVarRefType) core.Value { 40 | match glob_typ { 41 | .printf_str { 42 | return em.global_const[glob_typ] or { 43 | str_ref := em.bld.create_global_string_ptr('%s', '') 44 | em.global_const[glob_typ] = str_ref 45 | str_ref 46 | } 47 | } 48 | .printf_str_nl { 49 | return em.global_const[glob_typ] or { 50 | str_ref := em.bld.create_global_string_ptr('%s\n', '') 51 | em.global_const[glob_typ] = str_ref 52 | str_ref 53 | } 54 | } 55 | .printf_num { 56 | return em.global_const[glob_typ] or { 57 | str_ref := em.bld.create_global_string_ptr('%d', '') 58 | em.global_const[glob_typ] = str_ref 59 | str_ref 60 | } 61 | } 62 | .str_true { 63 | return em.global_const[glob_typ] or { 64 | str_ref := em.bld.create_global_string_ptr('true', '') 65 | em.global_const[glob_typ] = str_ref 66 | str_ref 67 | } 68 | } 69 | .str_false { 70 | return em.global_const[glob_typ] or { 71 | str_ref := em.bld.create_global_string_ptr('false', '') 72 | em.global_const[glob_typ] = str_ref 73 | str_ref 74 | } 75 | } 76 | .nl { 77 | return em.global_const[glob_typ] or { 78 | str_ref := em.bld.create_global_string_ptr('\n', '') 79 | em.global_const[glob_typ] = str_ref 80 | str_ref 81 | } 82 | } 83 | else {} 84 | } 85 | panic('unexepected, missing handle of global string') 86 | } 87 | -------------------------------------------------------------------------------- /lib/comp/gen/llvm/emit/emit_tests.v: -------------------------------------------------------------------------------- 1 | module emit 2 | 3 | import term 4 | 5 | pub fn (mut em EmitModule) run_tests() bool { 6 | em.init_execution_engine() or { panic('error init execution enging : $err.msg') } 7 | if em.exec_engine == 0 { 8 | panic('unexpected, execution engine have to be initialized before calling run_main') 9 | } 10 | mut test_funcs := []FunctionDecl{} 11 | for func in em.funcs { 12 | if func.name.starts_with('test_') { 13 | test_funcs << func 14 | } 15 | } 16 | 17 | // test_funcs.sort_with_compare(compare_function_by_file_and_name) 18 | 19 | // run main to be sure it is jit compiled 20 | em.exec_engine.run_function(em.main_func_val) 21 | 22 | mut nr_of_tests := 0 23 | mut nr_of_errors := 0 24 | mut current_file_has_errors := false 25 | mut current_file := '' 26 | mut total_nr_of_test_files := 0 27 | 28 | for func in test_funcs { 29 | if func.func.location.source.filename != current_file { 30 | total_nr_of_test_files++ 31 | current_file = func.func.location.source.filename 32 | } 33 | } 34 | current_file = '' 35 | println('------------------------------ test ------------------------------') 36 | for func in test_funcs { 37 | if func.func.location.source.filename != current_file { 38 | if current_file.len > 0 { 39 | print_result(current_file, !current_file_has_errors, nr_of_tests, total_nr_of_test_files) 40 | } 41 | current_file = func.func.location.source.filename 42 | current_file_has_errors = false 43 | nr_of_tests++ 44 | } 45 | test_res := em.exec_engine.run_function(func.val) 46 | int_res := i64(test_res.int(true)) 47 | if int_res == 0 { 48 | } else { 49 | current_file_has_errors = true 50 | nr_of_errors++ 51 | } 52 | } 53 | print_result(current_file, !current_file_has_errors, nr_of_tests, total_nr_of_test_files) 54 | 55 | println('------------------------------------------------------------------') 56 | return nr_of_errors == 0 57 | } 58 | 59 | fn print_result(filename string, is_ok bool, test_nr int, total_nr_of_tests int) { 60 | print(' ') 61 | if is_ok { 62 | print(term.green(' OK ')) 63 | } else { 64 | print(term.fail_message('FAIL')) 65 | } 66 | print(' [') 67 | total_nr_of_digits_total := nr_of_digits(total_nr_of_tests) 68 | total_nr_of_digits_test_nr := nr_of_digits(test_nr) 69 | leading_zeros := total_nr_of_digits_total - total_nr_of_digits_test_nr 70 | if leading_zeros > 0 { 71 | print('0'.repeat(leading_zeros)) 72 | } 73 | print('$test_nr/$total_nr_of_tests] ') 74 | println(filename) 75 | } 76 | 77 | fn nr_of_digits(n int) int { 78 | mut total := 0 79 | for i := 1; i <= n; i *= 10 { 80 | total++ 81 | } 82 | return total 83 | } 84 | 85 | // fn compare_function_by_file_and_name(a &FunctionDecl, b &FunctionDecl) int { 86 | // println('COMPARE : ${voidptr(a.func)}, ${voidptr(b.func)}') 87 | // if a.func.location.source.filename == b.func.location.source.filename { 88 | // if a.func.name < b.func.name { 89 | // return -1 90 | // } else { 91 | // return 1 92 | // } 93 | // } 94 | 95 | // if a.func.location.source.filename < b.func.location.source.filename { 96 | // return -1 97 | // } 98 | // return 1 99 | // } 100 | -------------------------------------------------------------------------------- /lib/comp/gen/llvm/emit/emit_types.v: -------------------------------------------------------------------------------- 1 | module emit 2 | 3 | import lib.comp.symbols 4 | import lib.comp.gen.llvm.core 5 | 6 | [inline] 7 | fn (em &EmitModule) get_ref_type_from_type_symb(symb_typ symbols.TypeSymbol) core.Type { 8 | mut typ := em.get_type_from_type_symb(symb_typ) 9 | if symb_typ.is_ref { 10 | typ = typ.to_pointer_type(0) 11 | } 12 | return typ 13 | } 14 | 15 | fn (em &EmitModule) get_type_from_type_symb(typ symbols.TypeSymbol) core.Type { 16 | match typ { 17 | symbols.BuiltInTypeSymbol { 18 | match typ.kind { 19 | .int_symbol { 20 | return em.ctx.int32_type() 21 | } 22 | .i64_symbol { 23 | return em.ctx.int64_type() 24 | } 25 | .bool_symbol { 26 | return em.ctx.int1_type() 27 | } 28 | .string_symbol { 29 | return em.ctx.int8_type().to_pointer_type(0) 30 | } 31 | .byte_symbol { 32 | return em.ctx.int8_type() 33 | } 34 | .char_symbol { 35 | return em.ctx.int8_type() 36 | } 37 | else { 38 | panic('unexpected, unsupported built-in type: $typ') 39 | } 40 | } 41 | } 42 | symbols.ArrayTypeSymbol { 43 | elem_typ := em.get_type_from_type_symb(typ.elem_typ) 44 | return elem_typ.to_array_type(typ.len) 45 | } 46 | symbols.VoidTypeSymbol { 47 | return em.ctx.void_type() 48 | } 49 | symbols.StructTypeSymbol { 50 | return em.types[typ.name] or { 51 | panic('unexpected, type $typ.name not found in symols table $em.types.keys()') 52 | } 53 | } 54 | else { 55 | panic('unexpected, unsupported type ref $typ, $typ.kind') 56 | } 57 | } 58 | 59 | panic('unexpected, unsupported type: $typ') 60 | } 61 | -------------------------------------------------------------------------------- /lib/comp/gen/llvm/llvm_gen.v: -------------------------------------------------------------------------------- 1 | module llvm 2 | 3 | import os 4 | import lib.comp.util.source 5 | import lib.comp.util.pref 6 | import lib.comp.binding 7 | // import lib.comp.gen.llvm.core 8 | import lib.comp.gen.llvm.emit 9 | 10 | struct LlvmGen { 11 | mut: 12 | log &source.Diagnostics 13 | mod &emit.EmitModule 14 | pref pref.CompPref 15 | program &binding.BoundProgram = 0 16 | result_file_full_path string 17 | optimize_jit bool // true run optimize passes for JIT 18 | optimize_aheadot bool // true run optimize passes on ahead of time compile 19 | } 20 | 21 | pub fn new_llvm_generator() LlvmGen { 22 | return LlvmGen{ 23 | log: source.new_diagonistics() 24 | mod: emit.new_emit_module('program') 25 | } 26 | } 27 | 28 | pub fn (mut l LlvmGen) generate(pref pref.CompPref, program &binding.BoundProgram) &source.Diagnostics { 29 | l.program = program 30 | l.pref = pref 31 | binary_directory := os.dir(pref.output) 32 | binary_name := os.file_name(pref.output) 33 | l.result_file_full_path = os.join_path(binary_directory, '${binary_name}.bc') 34 | l.optimize_aheadot = true // only optimize ahead of time 35 | // Generate code 36 | l.generate_code(false) 37 | 38 | if l.log.all.len > 0 { 39 | return l.log 40 | } 41 | // Compile generated code 42 | l.compile_code() 43 | 44 | l.mod.dispose() 45 | return l.log 46 | } 47 | 48 | pub fn (mut l LlvmGen) run(program &binding.BoundProgram, pref pref.CompPref) &source.Diagnostics { 49 | l.program = program 50 | l.pref = pref 51 | l.result_file_full_path = 'generated.ll' 52 | l.generate_code(false) 53 | l.mod.run_main() 54 | l.mod.dispose() 55 | return l.log 56 | } 57 | 58 | pub fn (mut l LlvmGen) run_tests(program &binding.BoundProgram, pref pref.CompPref) &source.Diagnostics { 59 | l.program = program 60 | l.pref = pref 61 | l.result_file_full_path = 'generated.ll' 62 | l.generate_code(true) 63 | res := l.mod.run_tests() 64 | if !res { 65 | l.log.error_msg('test failed') 66 | } 67 | 68 | l.mod.dispose() 69 | return l.log 70 | } 71 | 72 | fn (mut l LlvmGen) generate_code(is_test bool) { 73 | l.mod.generate_module(l.program, is_test) 74 | if l.optimize_aheadot && l.pref.is_prod { 75 | l.mod.optimize() 76 | } 77 | if l.pref.print_ll { 78 | l.mod.print_to_file('${l.result_file_full_path}.ll') or { 79 | panic('unexpected error cannot print to file') 80 | } 81 | } 82 | l.mod.verify() or { panic('unexpected error generating llvm code') } 83 | } 84 | 85 | fn (mut l LlvmGen) compile_code() { 86 | // generate the bitcode file 87 | l.mod.write_to_file(l.result_file_full_path) or { 88 | panic('unexpected error cannot write to file') 89 | } 90 | binary_directory := os.dir(l.pref.output) 91 | binary_name := os.file_name(l.pref.output) 92 | optimize := if l.pref.is_prod { '-O2' } else { '-O0' } 93 | compile_command := 'llc $optimize $l.result_file_full_path' 94 | s_file := os.join_path(binary_directory, '${binary_name}.s') 95 | gen_executable_command := 'clang $optimize -o $l.pref.output $s_file' 96 | res := os.execute(compile_command) 97 | if res.exit_code != 0 { 98 | l.log.error_msg(res.output) 99 | } 100 | res_exe := os.execute(gen_executable_command) 101 | if res_exe.exit_code != 0 { 102 | l.log.error_msg(res.output) 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /lib/comp/gen/llvm/test.c: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | int main() { 4 | printf("hello world\n"); 5 | return 0; 6 | } -------------------------------------------------------------------------------- /lib/comp/grammar.md: -------------------------------------------------------------------------------- 1 | # Specification for the micro V language 2 | TODO: everything 3 | ## TOKENS 4 | 5 | | Type | tokens | 6 | | ----- | ------------- | 7 | | Reserved words | `for` | 8 | | ident | any identifier| 9 | | string literal | "hello", 'hello'| 10 | | comment token | a code comment| 11 | | end of file (EOF) | end of file | 12 | | single character operators | `':'` | 13 | -------------------------------------------------------------------------------- /lib/comp/io/code_writer.v: -------------------------------------------------------------------------------- 1 | module io 2 | import strings 3 | 4 | pub interface CodeWriter { 5 | write_space() 6 | writeln(s string) 7 | write(s string) 8 | // write_diagnostics(diagnostics []&source.Diagnostic, syntax_tree ast.SyntaxTree) 9 | indent_add(n int) 10 | indent() int 11 | } 12 | 13 | pub struct GeneralCodeWriter { 14 | mut: 15 | current_indent int 16 | builder strings.Builder = strings.new_builder(0) 17 | write_indent bool = true 18 | } 19 | pub fn new_general_code_writer() GeneralCodeWriter { 20 | return GeneralCodeWriter {} 21 | } 22 | pub fn (mut gcw GeneralCodeWriter) write_space() { 23 | gcw.write(' ') 24 | } 25 | pub fn (mut gcw GeneralCodeWriter) writeln(s string) { 26 | gcw.write_indent() 27 | gcw.builder.writeln(s) 28 | gcw.write_indent = true 29 | } 30 | pub fn (mut gcw GeneralCodeWriter) write(s string) { 31 | gcw.write_indent() 32 | gcw.builder.write_string(s) 33 | } 34 | 35 | pub fn (mut gcw GeneralCodeWriter) indent_add(n int) { 36 | gcw.current_indent = gcw.current_indent + n 37 | } 38 | pub fn (mut gcw GeneralCodeWriter) indent() int { 39 | return gcw.current_indent 40 | } 41 | 42 | pub fn (mut gcw GeneralCodeWriter) str() string { 43 | return gcw.builder.str() 44 | } 45 | 46 | [inline] 47 | fn (mut gcw GeneralCodeWriter) write_indent() { 48 | if gcw.write_indent && gcw.current_indent > 0 { 49 | gcw.builder.write_string('\t'.repeat(gcw.current_indent)) 50 | gcw.write_indent = false 51 | } 52 | } -------------------------------------------------------------------------------- /lib/comp/io/node_string_writer.v: -------------------------------------------------------------------------------- 1 | module io 2 | 3 | import strings 4 | // import lib.comp.util.source 5 | // import lib.comp.parser 6 | 7 | struct NodeStringWriter { 8 | mut: 9 | b strings.Builder = strings.new_builder(0) 10 | } 11 | 12 | pub fn new_node_string_writer() NodeStringWriter { 13 | return NodeStringWriter{} 14 | } 15 | 16 | pub fn (mut w NodeStringWriter) str() string { 17 | return w.b.str() 18 | } 19 | 20 | pub fn (mut w NodeStringWriter) write_keyword(s string) { 21 | w.b.write_string(s) 22 | } 23 | 24 | pub fn (mut w NodeStringWriter) write_comment(s string) { 25 | w.b.write_string(s) 26 | } 27 | 28 | pub fn (mut w NodeStringWriter) write_string(s string) { 29 | w.b.write_string(s) 30 | } 31 | 32 | pub fn (mut w NodeStringWriter) write_identifier(s string) { 33 | w.b.write_string(s) 34 | } 35 | 36 | pub fn (mut w NodeStringWriter) write_punctuation(s string) { 37 | w.b.write_string(s) 38 | } 39 | 40 | pub fn (mut w NodeStringWriter) write_number(s string) { 41 | w.b.write_string(s) 42 | } 43 | 44 | pub fn (mut w NodeStringWriter) write_space() { 45 | w.b.write_string(' ') 46 | } 47 | 48 | pub fn (mut w NodeStringWriter) writeln(s string) { 49 | w.b.writeln(s) 50 | } 51 | 52 | pub fn (mut w NodeStringWriter) write(s string) { 53 | w.b.write_string(s) 54 | } 55 | 56 | pub fn (mut w NodeStringWriter) indent_add(n int) { 57 | } 58 | 59 | pub fn (mut w NodeStringWriter) indent() int { 60 | return 0 61 | } 62 | -------------------------------------------------------------------------------- /lib/comp/io/string_writer.v: -------------------------------------------------------------------------------- 1 | module io 2 | 3 | import strings 4 | 5 | struct StringWriter { 6 | mut: 7 | sb strings.Builder = strings.new_builder(0) 8 | } 9 | 10 | pub fn new_string_writer() StringWriter { 11 | return StringWriter{} 12 | } 13 | 14 | pub fn (mut sw StringWriter) writeln(s string) { 15 | sw.sb.writeln(s) 16 | } 17 | 18 | pub fn (mut sw StringWriter) write_string(s string) { 19 | sw.sb.write_string(s) 20 | } 21 | -------------------------------------------------------------------------------- /lib/comp/io/text_writer.v: -------------------------------------------------------------------------------- 1 | module io 2 | 3 | // import lib.comp.util.source 4 | // import lib.comp.parser 5 | 6 | pub interface TermTextWriter { 7 | write_keyword(s string) 8 | write_string(s string) 9 | write_comment(s string) 10 | write_identifier(s string) 11 | write_punctuation(s string) 12 | write_number(s string) 13 | write_space() 14 | writeln(s string) 15 | write(s string) 16 | // write_diagnostics(diagnostics []&source.Diagnostic, syntax_tree ast.SyntaxTree) 17 | indent_add(n int) 18 | indent() int 19 | } 20 | 21 | pub interface TextWriter { 22 | writeln(s string) ?int 23 | write_string(s string) ?int 24 | } 25 | -------------------------------------------------------------------------------- /lib/comp/parser/pattern_match.v: -------------------------------------------------------------------------------- 1 | module parser 2 | 3 | import lib.comp.token 4 | 5 | // peek_var_dec matches 'name = expr' 6 | fn (mut p Parser) peek_var_decl(n int) bool { 7 | mut i := n 8 | mut expected_kind := token.Kind.name 9 | // we expect a valid name expr to be name and dots 10 | for { 11 | if p.peek_token(i).kind != expected_kind { 12 | return false 13 | } 14 | if p.peek_token(i + 1).kind == .colon_eq { 15 | return true 16 | } 17 | i += 1 18 | expected_kind = if expected_kind == .name { token.Kind.dot } else { token.Kind.name } 19 | } 20 | return false 21 | } 22 | 23 | // peek_assignment matches 'name = expr' 24 | fn (mut p Parser) peek_assignment(n int) bool { 25 | mut i := n 26 | mut expected_kind := token.Kind.name 27 | // we expect a valid name expr to be name and dots 28 | for { 29 | if p.peek_token(i).kind != expected_kind { 30 | return false 31 | } 32 | if p.peek_token(i + 1).kind == .eq { 33 | return true 34 | } 35 | i += 1 36 | expected_kind = if expected_kind == .name { token.Kind.dot } else { token.Kind.name } 37 | } 38 | return false 39 | } 40 | 41 | // peek_assignment matches 'fn ident(..){}' 42 | fn (mut p Parser) peek_fn_decl(n int) bool { 43 | return p.peek_token(n).kind == .key_fn 44 | || (p.peek_token(n).kind == .key_mut && p.peek_token(n + 1).kind == .key_fn) 45 | } 46 | 47 | // peek_assignment matches 'struct x { element type ...}' 48 | fn (mut p Parser) peek_struct_decl(n int) bool { 49 | return p.peek_token(n).kind == .key_struct && p.peek_token(n + 1).kind == .name 50 | } 51 | 52 | // peek_assignment matches ' Type{ member: type ...}' 53 | fn (mut p Parser) peek_struct_init(n int) bool { 54 | return p.peek_token(n).kind == .name && p.peek_token(n + 1).kind == .lcbr 55 | } 56 | -------------------------------------------------------------------------------- /lib/comp/parser/precedence_test.v: -------------------------------------------------------------------------------- 1 | module parser 2 | 3 | import lib.comp.ast 4 | 5 | fn test_binary_operator_precedence() { 6 | assert ast.binary_operator_precedence(.plus) == ast.binary_operator_precedence(.minus) 7 | assert ast.binary_operator_precedence(.div) == ast.binary_operator_precedence(.mul) 8 | assert ast.binary_operator_precedence(.lt) == ast.binary_operator_precedence(.eq_eq) 9 | assert ast.binary_operator_precedence(.gt) == ast.binary_operator_precedence(.eq_eq) 10 | assert ast.binary_operator_precedence(.gt_eq) == ast.binary_operator_precedence(.eq_eq) 11 | assert ast.binary_operator_precedence(.lt_eq) == ast.binary_operator_precedence(.eq_eq) 12 | // only need to test this combination since mul/div and plus/minus tested same 13 | assert ast.binary_operator_precedence(.mul) > ast.binary_operator_precedence(.plus) 14 | // unary operator 15 | assert ast.unary_operator_precedence(.minus) > ast.binary_operator_precedence(.plus) 16 | assert ast.unary_operator_precedence(.plus) > ast.binary_operator_precedence(.plus) 17 | assert ast.unary_operator_precedence(.minus) > ast.binary_operator_precedence(.plus) 18 | assert ast.unary_operator_precedence(.exl_mark) > ast.binary_operator_precedence(.plus) 19 | assert ast.unary_operator_precedence(.exl_mark) == ast.unary_operator_precedence(.plus) 20 | assert ast.unary_operator_precedence(.exl_mark) == ast.unary_operator_precedence(.minus) 21 | } 22 | -------------------------------------------------------------------------------- /lib/comp/readme.md: -------------------------------------------------------------------------------- 1 | # Parser docs 2 | todo: -------------------------------------------------------------------------------- /lib/comp/symbols/built_in.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | pub const ( 4 | // symbols 5 | int_symbol = new_builtin_type_symbol(.int_symbol, 'int') 6 | byte_symbol = new_builtin_type_symbol(.byte_symbol, 'byte') 7 | char_symbol = new_builtin_type_symbol(.char_symbol, 'char') 8 | i64_symbol = new_builtin_type_symbol(.i64_symbol, 'i64') 9 | bool_symbol = new_builtin_type_symbol(.bool_symbol, 'bool') 10 | string_symbol = new_builtin_type_symbol(.string_symbol, 'string') 11 | undefined_symbol = new_builtin_type_symbol(.undefined_symbol, 'undefined') 12 | none_symbol = new_builtin_type_symbol(.none_symbol, 'none') 13 | // C interoptyp, 14 | voidptr_symbol = new_builtin_type_symbol(.voidptr_symbol, 'voidptr') 15 | 16 | any_symbol = new_any_type_symbol(false) 17 | error_symbol = new_error_type_symbol() 18 | void_symbol = new_void_type_symbol() 19 | empty_var_symbol = new_empty_local_variable_symbol() 20 | 21 | builtin_types = builtin_types() 22 | 23 | // built-in function symbols 24 | println_symbol = new_function_symbol('lib.runtime', 'println', [ 25 | new_param_symbol('text', string_symbol, false, false, false), 26 | ], void_symbol) 27 | print_symbol = new_function_symbol('lib.runtime', 'print', [ 28 | new_param_symbol('text', string_symbol, false, false, false), 29 | ], void_symbol) 30 | exit_symbol = new_function_symbol('lib.runtime', 'exit', [ 31 | new_param_symbol('exit_code', int_symbol, false, false, false), 32 | ], void_symbol) 33 | input_symbol = new_function_symbol('lib.runtime', 'input', [], string_symbol) 34 | built_in_functions = [println_symbol, print_symbol, input_symbol, exit_symbol] 35 | ) 36 | 37 | pub fn builtin_types() []TypeSymbol { 38 | return [ 39 | TypeSymbol(symbols.int_symbol), 40 | symbols.byte_symbol, 41 | symbols.char_symbol, 42 | symbols.i64_symbol, 43 | symbols.bool_symbol, 44 | symbols.string_symbol, 45 | symbols.undefined_symbol, 46 | symbols.none_symbol, 47 | symbols.voidptr_symbol, 48 | ] 49 | } 50 | -------------------------------------------------------------------------------- /lib/comp/symbols/symb_const.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import rand 4 | import lib.comp.symbols 5 | 6 | pub struct ConstSymbol { 7 | pub: 8 | id string 9 | typ TypeSymbol 10 | val LitVal 11 | } 12 | 13 | pub fn (cs ConstSymbol) str() string { 14 | return '$cs.val' 15 | } 16 | 17 | pub fn (cs ConstSymbol) str_ident(level int) string { 18 | return '$cs.val' 19 | } 20 | 21 | pub fn new_const_symbol(val LitVal) ConstSymbol { 22 | return symbols.ConstSymbol{ 23 | typ: val.typ() 24 | val: val 25 | id: rand.uuid_v4() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/comp/symbols/symb_function.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import rand 4 | import lib.comp.util.source 5 | 6 | pub const ( 7 | undefined_fn = FunctionSymbol{ 8 | mod: 'lib.runtime' 9 | name: 'undefined' 10 | id: 'undefined' 11 | } 12 | ) 13 | 14 | pub struct FunctionSymbol { 15 | pub: 16 | is_c_decl bool 17 | is_pub bool 18 | location source.TextLocation 19 | mod string 20 | name string 21 | typ TypeSymbol 22 | receiver LocalVariableSymbol 23 | params []ParamSymbol 24 | id string 25 | } 26 | 27 | pub fn (ts FunctionSymbol) str() string { 28 | return ts.unique_fn_name() 29 | } 30 | 31 | pub fn new_emtpy_function_symbol() FunctionSymbol { 32 | return FunctionSymbol{} 33 | } 34 | 35 | pub fn new_function_symbol(mod string, name string, params []ParamSymbol, typ TypeSymbol) FunctionSymbol { 36 | return FunctionSymbol{ 37 | mod: mod 38 | name: name 39 | params: params 40 | typ: typ 41 | receiver: new_empty_local_variable_symbol() 42 | id: rand.uuid_v4() 43 | } 44 | } 45 | 46 | pub fn new_function_symbol_on_heap(mod string, name string, params []ParamSymbol, typ TypeSymbol) &FunctionSymbol { 47 | return &FunctionSymbol{ 48 | mod: mod 49 | name: name 50 | params: params 51 | typ: typ 52 | receiver: new_empty_local_variable_symbol() 53 | id: rand.uuid_v4() 54 | } 55 | } 56 | 57 | pub fn new_function_symbol_from_decl(location source.TextLocation, mod string, receiver LocalVariableSymbol, name string, params []ParamSymbol, typ TypeSymbol, is_pub bool, is_c_decl bool) FunctionSymbol { 58 | return FunctionSymbol{ 59 | location: location 60 | mod: mod 61 | is_pub: is_pub 62 | is_c_decl: is_c_decl 63 | name: name 64 | receiver: receiver 65 | params: params 66 | typ: typ 67 | id: rand.uuid_v4() 68 | } 69 | } 70 | 71 | pub fn (ts FunctionSymbol) unique_fn_name() string { 72 | if ts.receiver.is_empty { 73 | return '${ts.mod}.$ts.name' 74 | } else { 75 | return ts.receiver.typ.unique_reciver_func_name(ts.name) 76 | } 77 | } 78 | 79 | pub fn lookup_built_in_function(name string) ?FunctionSymbol { 80 | for f in built_in_functions { 81 | if f.name == name { 82 | return f 83 | } 84 | } 85 | return none 86 | } 87 | -------------------------------------------------------------------------------- /lib/comp/symbols/symb_param.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import rand 4 | 5 | pub struct ParamSymbol { 6 | pub: 7 | name string 8 | typ TypeSymbol 9 | is_mut bool 10 | is_variadic bool 11 | id string 12 | is_ref bool 13 | } 14 | 15 | pub fn (ts ParamSymbol) == (rts ParamSymbol) bool { 16 | return ts.name == rts.name 17 | } 18 | 19 | pub fn (ts ParamSymbol) str() string { 20 | return ts.name 21 | } 22 | 23 | pub fn (ts ParamSymbol) str_ident(level int) string { 24 | ident := ' '.repeat(level) 25 | mut_str := if ts.is_mut { 'mut ' } else { '' } 26 | return '${ident}var: $mut_str <$ts.name> ($ts.typ.name)' 27 | } 28 | 29 | pub fn new_param_symbol(name string, typ TypeSymbol, is_mut bool, is_variadic bool, is_ref bool) ParamSymbol { 30 | return ParamSymbol{ 31 | name: name 32 | typ: typ 33 | is_mut: is_mut 34 | is_variadic: is_variadic 35 | is_ref: is_mut || typ.is_ref 36 | id: rand.uuid_v4() 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /lib/comp/symbols/symb_type_any.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import rand 4 | 5 | pub struct AnyTypeSymbol { 6 | pub: 7 | kind TypeSymbolKind 8 | mod string = 'lib.runtime' 9 | name string = 'any' 10 | id string 11 | is_ref bool 12 | } 13 | 14 | pub fn (ts AnyTypeSymbol) == (rts AnyTypeSymbol) bool { 15 | return ts.name == rts.name 16 | } 17 | 18 | pub fn (ts AnyTypeSymbol) str() string { 19 | return ts.name 20 | } 21 | 22 | pub fn (ss AnyTypeSymbol) to_ref_type() AnyTypeSymbol { 23 | return AnyTypeSymbol{ 24 | ...ss 25 | is_ref: true 26 | } 27 | } 28 | 29 | pub fn new_any_type_symbol(is_ref bool) AnyTypeSymbol { 30 | return AnyTypeSymbol{ 31 | kind: .any_symbol 32 | id: rand.uuid_v4() 33 | is_ref: is_ref 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/comp/symbols/symb_type_array.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import rand 4 | 5 | pub struct ArrayTypeSymbol { 6 | pub: 7 | kind TypeSymbolKind 8 | mod string = 'lib.runtime' 9 | name string 10 | id string 11 | is_ref bool 12 | 13 | elem_typ TypeSymbol 14 | len int 15 | is_fixed bool 16 | is_val_array bool 17 | } 18 | 19 | // pub fn (ss ArrayTypeSymbol) == (rss ArrayTypeSymbol) bool { 20 | // return ss.elem_typ.name == rss.elem_typ.name 21 | // } 22 | 23 | pub fn (ss ArrayTypeSymbol) str() string { 24 | return ss.name 25 | } 26 | 27 | pub fn (ss &ArrayTypeSymbol) to_ref_type() ArrayTypeSymbol { 28 | unbox := *ss 29 | return ArrayTypeSymbol{ 30 | ...unbox 31 | is_ref: true 32 | } 33 | } 34 | 35 | pub fn new_fixed_val_array_symbol(elem_typ TypeSymbol, len int, is_ref bool) ArrayTypeSymbol { 36 | return ArrayTypeSymbol{ 37 | kind: .array_symbol 38 | name: '[$len]$elem_typ.name' 39 | id: rand.uuid_v4() 40 | elem_typ: elem_typ 41 | is_ref: is_ref 42 | len: len 43 | is_fixed: true 44 | is_val_array: true 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /lib/comp/symbols/symb_type_built_in.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import rand 4 | 5 | pub struct BuiltInTypeSymbol { 6 | pub: 7 | kind TypeSymbolKind = .undefined_symbol 8 | mod string = 'lib.runtime' 9 | name string 10 | id string 11 | is_ref bool 12 | } 13 | 14 | pub fn (ts BuiltInTypeSymbol) == (rts BuiltInTypeSymbol) bool { 15 | return ts.kind == rts.kind 16 | } 17 | 18 | pub fn (ts BuiltInTypeSymbol) str() string { 19 | return ts.name 20 | } 21 | 22 | pub fn (ss BuiltInTypeSymbol) to_ref_type() BuiltInTypeSymbol { 23 | return BuiltInTypeSymbol{ 24 | ...ss 25 | is_ref: true 26 | } 27 | } 28 | 29 | pub fn new_builtin_type_symbol(kind TypeSymbolKind, name string) BuiltInTypeSymbol { 30 | return BuiltInTypeSymbol{ 31 | kind: kind 32 | name: name 33 | id: rand.uuid_v4() 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /lib/comp/symbols/symb_type_error.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import rand 4 | 5 | pub struct ErrorTypeSymbol { 6 | pub: 7 | kind TypeSymbolKind 8 | mod string = 'lib.runtime' 9 | name string = 'error' 10 | id string 11 | is_ref bool 12 | } 13 | 14 | pub fn (ts ErrorTypeSymbol) == (rts ErrorTypeSymbol) bool { 15 | return ts.name == rts.name 16 | } 17 | 18 | pub fn (ts ErrorTypeSymbol) str() string { 19 | return ts.name 20 | } 21 | 22 | pub fn new_error_type_symbol() ErrorTypeSymbol { 23 | return ErrorTypeSymbol{ 24 | kind: .error_symbol 25 | id: rand.uuid_v4() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/comp/symbols/symb_type_struct.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import rand 4 | 5 | pub struct StructTypeSymbol { 6 | pub: 7 | kind TypeSymbolKind 8 | mod string 9 | name string 10 | id string 11 | is_ref bool 12 | is_c_decl bool 13 | pub mut: 14 | members []StructTypeMember 15 | } 16 | 17 | pub fn (ss StructTypeSymbol) == (rss StructTypeSymbol) bool { 18 | return ss.name == rss.name 19 | } 20 | 21 | pub fn (ss StructTypeSymbol) str() string { 22 | return ss.name 23 | } 24 | 25 | pub fn (ss StructTypeSymbol) to_ref_type() StructTypeSymbol { 26 | return StructTypeSymbol{ 27 | ...ss 28 | is_ref: true 29 | } 30 | } 31 | 32 | pub fn new_struct_symbol(mod string, name string, is_ref bool, is_c_decl bool) StructTypeSymbol { 33 | return StructTypeSymbol{ 34 | kind: .struct_symbol 35 | mod: mod 36 | name: name 37 | is_c_decl: is_c_decl 38 | id: rand.uuid_v4() 39 | } 40 | } 41 | 42 | pub struct StructTypeMember { 43 | pub: 44 | ident string 45 | typ TypeSymbol 46 | is_ref bool 47 | } 48 | 49 | pub fn new_struct_type_member(ident string, typ TypeSymbol) StructTypeMember { 50 | return StructTypeMember{ 51 | ident: ident 52 | typ: typ 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /lib/comp/symbols/symb_type_void.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import rand 4 | 5 | pub struct VoidTypeSymbol { 6 | pub: 7 | kind TypeSymbolKind 8 | mod string = 'lib.runtime' 9 | name string = 'void' 10 | id string 11 | is_ref bool 12 | } 13 | 14 | pub fn (ts VoidTypeSymbol) == (rts VoidTypeSymbol) bool { 15 | return ts.name == rts.name 16 | } 17 | 18 | pub fn (ts VoidTypeSymbol) str() string { 19 | return ts.name 20 | } 21 | 22 | pub fn new_void_type_symbol() VoidTypeSymbol { 23 | return VoidTypeSymbol{ 24 | kind: .void_symbol 25 | id: rand.uuid_v4() 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /lib/comp/symbols/symb_variable.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import rand 4 | import lib.comp.symbols 5 | 6 | pub struct LocalVariableSymbol { 7 | pub: 8 | name string 9 | typ TypeSymbol 10 | mod string 11 | is_mut bool 12 | is_ref bool 13 | id string 14 | is_empty bool = true 15 | } 16 | 17 | pub fn (vs LocalVariableSymbol) str() string { 18 | mut_str := if vs.is_mut { 'mut ' } else { '' } 19 | return 'var: $mut_str <$vs.name> ($vs.typ.name)' 20 | } 21 | 22 | pub fn (vs LocalVariableSymbol) str_ident(level int) string { 23 | ident := ' '.repeat(level) 24 | mut_str := if vs.is_mut { 'mut ' } else { '' } 25 | return '${ident}var: $mut_str <$vs.name> ($vs.typ.name)' 26 | } 27 | 28 | pub fn new_empty_local_variable_symbol() LocalVariableSymbol { 29 | return symbols.LocalVariableSymbol{ 30 | is_empty: true 31 | typ: undefined_symbol 32 | } 33 | } 34 | 35 | pub fn new_local_variable_symbol(mod string, name string, typ TypeSymbol, is_mut bool) LocalVariableSymbol { 36 | return symbols.LocalVariableSymbol{ 37 | mod: mod 38 | name: name 39 | typ: typ 40 | is_mut: is_mut 41 | is_ref: is_mut || typ.is_ref 42 | is_empty: false 43 | id: rand.uuid_v4() 44 | } 45 | } 46 | 47 | pub struct GlobalVariableSymbol { 48 | pub: 49 | name string 50 | mod string 51 | typ TypeSymbol 52 | is_mut bool 53 | is_ref bool 54 | id string 55 | } 56 | 57 | pub fn (vs GlobalVariableSymbol) str() string { 58 | mut_str := if vs.is_mut { 'mut ' } else { '' } 59 | return 'var: $mut_str <$vs.name> ($vs.typ.name)' 60 | } 61 | 62 | pub fn (vs GlobalVariableSymbol) str_ident(level int) string { 63 | ident := ' '.repeat(level) 64 | mut_str := if vs.is_mut { 'mut ' } else { '' } 65 | return '${ident}var: $mut_str <$vs.name> ($vs.typ.name)' 66 | } 67 | 68 | pub fn new_global_variable_symbol(mod string, name string, typ TypeSymbol, is_mut bool) GlobalVariableSymbol { 69 | return symbols.GlobalVariableSymbol{ 70 | mod: mod 71 | name: name 72 | typ: typ 73 | is_mut: is_mut 74 | is_ref: is_mut || typ.is_ref 75 | id: rand.uuid_v4() 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/comp/symbols/symbol_printer.v: -------------------------------------------------------------------------------- 1 | module symbols 2 | 3 | import lib.comp.io 4 | import lib.comp.symbols 5 | 6 | pub fn write_symbol(writer io.TermTextWriter, symbol Symbol) { 7 | match symbol { 8 | VariableSymbol { 9 | write_var_symbol(writer, symbol) 10 | } 11 | // BoundStmt { write_stmt(writer, node) } 12 | FunctionSymbol { 13 | write_function_symbol(writer, symbol) 14 | } 15 | ConstSymbol { 16 | write_const_symbol(writer, symbol) 17 | } 18 | TypeSymbol { 19 | write_type_symbol(writer, symbol) 20 | } 21 | } 22 | } 23 | fn write_type_symbol(writer io.TermTextWriter, type_symbol TypeSymbol) { 24 | match type_symbol { 25 | BuiltInTypeSymbol { 26 | write_builtin_type_symbol(writer, type_symbol) 27 | } 28 | StructTypeSymbol { 29 | write_struct_symbol(writer, type_symbol) 30 | } 31 | ErrorTypeSymbol { 32 | write_error_symbol(writer, type_symbol) 33 | } 34 | AnyTypeSymbol { 35 | write_any_symbol(writer, type_symbol) 36 | } 37 | ArrayTypeSymbol { 38 | write_array_symbol(writer, type_symbol) 39 | } 40 | VoidTypeSymbol { 41 | //ignore 42 | } 43 | } 44 | } 45 | fn write_param_symbol(writer io.TermTextWriter, param_symbol ParamSymbol) { 46 | if param_symbol.is_mut { 47 | writer.write_keyword('mut') 48 | writer.write_space() 49 | } 50 | writer.write_identifier(param_symbol.name) 51 | writer.write_space() 52 | write_type_symbol(writer, param_symbol.typ) 53 | } 54 | 55 | fn write_array_symbol(writer io.TermTextWriter, array_symbol ArrayTypeSymbol) { 56 | writer.write_identifier('[]') 57 | write_type_symbol(writer, array_symbol.elem_typ) 58 | } 59 | 60 | fn write_any_symbol(writer io.TermTextWriter, error_symbol AnyTypeSymbol) { 61 | writer.write_keyword('any') 62 | } 63 | 64 | fn write_error_symbol(writer io.TermTextWriter, error_symbol ErrorTypeSymbol) { 65 | writer.write_identifier('error') 66 | } 67 | fn write_struct_symbol(writer io.TermTextWriter, struct_symbol StructTypeSymbol) { 68 | writer.write_keyword('struct') 69 | writer.write_space() 70 | writer.write_identifier(struct_symbol.name) 71 | writer.write_space() 72 | writer.write_punctuation('{') 73 | for member in struct_symbol.members { 74 | writer.write_identifier(member.ident) 75 | writer.write_space() 76 | write_type_symbol(writer, member.typ) 77 | writer.writeln('') 78 | } 79 | writer.writeln('') 80 | writer.write_punctuation('}') 81 | writer.writeln('') 82 | } 83 | 84 | fn write_function_symbol(writer io.TermTextWriter, fn_symbol FunctionSymbol) { 85 | writer.write_keyword('fn') 86 | writer.write_space() 87 | writer.write_identifier(fn_symbol.name) 88 | writer.write_punctuation('(') 89 | for i, param in fn_symbol.params { 90 | if i != 0 { 91 | writer.write_punctuation(',') 92 | writer.write_space() 93 | } 94 | write_param_symbol(writer, param) 95 | } 96 | writer.write_punctuation(')') 97 | match fn_symbol.typ { 98 | symbols.BuiltInTypeSymbol { 99 | writer.write_space() 100 | write_type_symbol(writer, fn_symbol.typ) 101 | } 102 | symbols.StructTypeSymbol { 103 | writer.write_space() 104 | write_type_symbol(writer, fn_symbol.typ) 105 | } 106 | symbols.ArrayTypeSymbol { 107 | writer.write_space() 108 | writer.write_identifier('[]') 109 | write_type_symbol(writer, fn_symbol.typ) 110 | } 111 | symbols.ErrorTypeSymbol { 112 | writer.write_space() 113 | write_type_symbol(writer, fn_symbol.typ) 114 | } 115 | symbols.AnyTypeSymbol { 116 | writer.write_space() 117 | write_type_symbol(writer, fn_symbol.typ) 118 | } 119 | symbols.VoidTypeSymbol { 120 | // Ignore 121 | } 122 | } 123 | writer.writeln('') 124 | } 125 | 126 | fn write_builtin_type_symbol(writer io.TermTextWriter, type_symbol BuiltInTypeSymbol) { 127 | writer.write_identifier(type_symbol.name) 128 | } 129 | 130 | fn write_const_symbol(writer io.TermTextWriter, const_symbol ConstSymbol) { 131 | writer.write_identifier(const_symbol.val.str()) 132 | } 133 | 134 | fn write_var_symbol(writer io.TermTextWriter, var_symbol VariableSymbol) { 135 | match var_symbol { 136 | LocalVariableSymbol {} 137 | GlobalVariableSymbol {} 138 | ParamSymbol { 139 | write_param_symbol(writer, var_symbol) 140 | } 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /lib/comp/token/token.v: -------------------------------------------------------------------------------- 1 | module token 2 | 3 | import lib.comp.util.source as src 4 | 5 | pub const ( 6 | tok_void = Token{ 7 | kind: .void 8 | lit: 'void' 9 | source: src.new_source_text('') 10 | } 11 | nr_tokens = int(Kind._end_) 12 | token_str = build_token_str() 13 | keywords = build_keys() 14 | ) 15 | 16 | pub struct Token { 17 | pub: 18 | source &src.SourceText 19 | kind Kind // the token number/enum; for quick comparisons 20 | lit string // literal representation of the token 21 | pos src.Pos // position in the file 22 | } 23 | 24 | pub fn (t Token) str() string { 25 | return "tok: [$t.pos.pos, ($t.pos.len) $t.kind '$t.lit'" 26 | } 27 | 28 | pub fn (t Token) text_location() src.TextLocation { 29 | return src.new_text_location(t.source, t.pos) 30 | } 31 | 32 | pub fn (ex Token) node_str() string { 33 | return ex.lit 34 | } 35 | 36 | // Kind of token 37 | pub enum Kind { 38 | unknown 39 | bad_token // bad token 40 | name // Any identifier name 41 | number //[1-9]+ 42 | string // a string literal 43 | lsbr // '[' 44 | rsbr // ']' 45 | lcbr // '{' 46 | rcbr // '}' 47 | lpar // '(' 48 | rpar // ')' 49 | tilde // '~' 50 | colon // ':' 51 | semcol // ';' 52 | dot // '.' 53 | dot_dot // '..' 54 | dot_dot_dot // '...' 55 | comma // ',' 56 | eq // '=' 57 | gt // '>' 58 | lt // '<' 59 | colon_eq // ':=' 60 | plus // '+' 61 | minus // '-' 62 | mul // '*' 63 | div // '/' 64 | amp // '&' 65 | pipe // '|' 66 | eq_eq // '==' 67 | gt_eq // '>=' 68 | lt_eq // '<=' 69 | exl_mark_eq // '!=' 70 | exl_mark // '!' 71 | hat // '^' 72 | pipe_pipe // '||' 73 | amp_amp // '&&' 74 | eof // end of file 75 | void // used to non existin token 76 | comment // a code comment block 77 | // Keywords 78 | keyword_beg // start of keywords 79 | key_fn // 'fn' 80 | key_if // 'if' 81 | key_else // 'else' 82 | key_continue // 'continue' 83 | key_break // 'break' 84 | key_return // 'return' 85 | key_module // 'module' 86 | key_import // 'import' 87 | key_struct // 'struct' 88 | key_assert // 'assert' 89 | key_true // 'true' 90 | key_false // 'false' 91 | key_mut // 'mut' 92 | key_for // 'for' 93 | key_in // 'in' 94 | key_pub // 'pub' 95 | keyword_end // end of keywords 96 | _end_ // end of enum 97 | } 98 | 99 | pub fn build_keys() map[string]Kind { 100 | mut res := map[string]Kind{} 101 | for t in int(Kind.keyword_beg) + 1 .. int(Kind.keyword_end) { 102 | key := token.token_str[t] 103 | res[key] = Kind(t) 104 | } 105 | return res 106 | } 107 | 108 | fn build_token_str() []string { 109 | mut s := []string{len: token.nr_tokens} 110 | s[Kind.unknown] = 'token_unknown' 111 | s[Kind.bad_token] = 'bad_token' 112 | s[Kind.eof] = 'eof' 113 | s[Kind.name] = '' // no default value 114 | s[Kind.string] = '' // no default value 115 | s[Kind.number] = '' // no default value 116 | s[Kind.eq] = '=' 117 | s[Kind.gt] = '>' 118 | s[Kind.lt] = '<' 119 | s[Kind.colon_eq] = ':=' 120 | s[Kind.eq_eq] = '==' 121 | s[Kind.lt_eq] = '<=' 122 | s[Kind.gt_eq] = '>=' 123 | s[Kind.exl_mark_eq] = '!=' 124 | s[Kind.plus] = '+' 125 | s[Kind.minus] = '-' 126 | s[Kind.mul] = '*' 127 | s[Kind.div] = '/' 128 | s[Kind.amp] = '&' 129 | s[Kind.pipe] = '|' 130 | s[Kind.lsbr] = '[' 131 | s[Kind.rsbr] = ']' 132 | s[Kind.lcbr] = '{' 133 | s[Kind.rcbr] = '}' 134 | s[Kind.lpar] = '(' 135 | s[Kind.rpar] = ')' 136 | s[Kind.tilde] = '~' 137 | s[Kind.hat] = '^' 138 | s[Kind.dot_dot] = '..' 139 | s[Kind.dot_dot_dot] = '...' 140 | s[Kind.comma] = ',' 141 | s[Kind.colon] = ':' 142 | s[Kind.semcol] = ';' 143 | s[Kind.pipe_pipe] = '||' 144 | s[Kind.amp_amp] = '&&' 145 | s[Kind.exl_mark] = '!' 146 | s[Kind.comment] = '' // no default value 147 | s[Kind.key_mut] = 'mut' 148 | s[Kind.key_true] = 'true' 149 | s[Kind.key_false] = 'false' 150 | s[Kind.key_fn] = 'fn' 151 | s[Kind.key_if] = 'if' 152 | s[Kind.key_else] = 'else' 153 | s[Kind.key_continue] = 'continue' 154 | s[Kind.key_break] = 'break' 155 | s[Kind.key_return] = 'return' 156 | s[Kind.key_module] = 'module' 157 | s[Kind.key_import] = 'import' 158 | s[Kind.key_struct] = 'struct' 159 | s[Kind.key_assert] = 'assert' 160 | s[Kind.key_for] = 'for' 161 | s[Kind.key_in] = 'in' 162 | s[Kind.key_pub] = 'pub' 163 | return s 164 | } 165 | -------------------------------------------------------------------------------- /lib/comp/util/annotated_text.v: -------------------------------------------------------------------------------- 1 | module util 2 | 3 | import strings 4 | import lib.comp.util.source 5 | 6 | pub struct AnnotatedText { 7 | pub: 8 | text string 9 | posns []source.Pos 10 | } 11 | 12 | fn new_annotated_text(text string, posns []source.Pos) AnnotatedText { 13 | return AnnotatedText{ 14 | text: text 15 | posns: posns 16 | } 17 | } 18 | 19 | pub fn parse_annotated_text(text string) AnnotatedText { 20 | mut b := strings.new_builder(20) 21 | uindent_text := unindent(text) 22 | mut posns := []source.Pos{cap: 20} 23 | mut fake_stack := []int{cap: 20} 24 | 25 | mut pos := 0 26 | for c in uindent_text { 27 | if c == `[` { 28 | fake_stack.prepend(pos) 29 | } else if c == `]` { 30 | if fake_stack.len == 0 { 31 | panic('unexpected error, missing "["?') 32 | } 33 | start := fake_stack.pop() 34 | end := pos 35 | posns << source.new_pos_from_bounds(start, end) 36 | } else { 37 | pos++ 38 | b.write_b(c) 39 | } 40 | } 41 | 42 | if fake_stack.len > 0 { 43 | panic('unexpected error, missing "]"?') 44 | } 45 | 46 | return AnnotatedText{ 47 | text: b.str() 48 | posns: posns 49 | } 50 | } 51 | 52 | pub fn unindent(text string) string { 53 | mut lines := unindent_lines(text) 54 | mut b := strings.new_builder(lines.len) 55 | for line in lines { 56 | if line == '' { 57 | continue 58 | } 59 | b.writeln(line) 60 | } 61 | return b.str() 62 | } 63 | 64 | pub fn unindent_lines(text string) []string { 65 | mut lines := text.split_into_lines() 66 | 67 | mut min_indent := int(0xfffffff) // some crazy big value 68 | for i, line in lines { 69 | if line.trim_space().len == 0 { 70 | lines[i] = '' 71 | continue 72 | } 73 | indent := line.len - line.trim_left(' \t').len 74 | min_indent = if indent < min_indent { indent } else { min_indent } 75 | lines[i] = line[min_indent..] 76 | } 77 | // trim start and end empty lines but keep in between 78 | for lines.len > 0 && lines[0].len == 0 { 79 | lines.delete(0) 80 | } 81 | for lines.len > 0 && lines[lines.len - 1].len == 0 { 82 | lines.delete(lines.len - 1) 83 | } 84 | return lines 85 | } 86 | -------------------------------------------------------------------------------- /lib/comp/util/annotated_text_test.v: -------------------------------------------------------------------------------- 1 | module util 2 | 3 | fn test_basic_annotation() { 4 | text := ' 5 | a:=1 6 | { 7 | b:=1 8 | } 9 | ' 10 | expected := 'a:=1 11 | { 12 | b:=1 13 | } 14 | ' 15 | assert expected == unindent(text) 16 | } 17 | -------------------------------------------------------------------------------- /lib/comp/util/mod/mod_lookup.v: -------------------------------------------------------------------------------- 1 | module mod 2 | 3 | import os 4 | 5 | const ( 6 | mod_file_stop_paths = ['.git', '.hg', '.svn', '.v.mod.stop'] 7 | ) 8 | 9 | [heap] 10 | pub struct ModuleCache { 11 | mut: 12 | modules map[string]string 13 | vmod_paths map[string]string 14 | pub mut: 15 | lib_path string 16 | current_dir string 17 | 18 | } 19 | 20 | pub fn new_mod_cache() &ModuleCache { 21 | file_dir := os.dir('${@FILE}') 22 | lib_path := file_dir[..file_dir.len-14] 23 | return &ModuleCache{ 24 | lib_path: lib_path 25 | current_dir: os.getwd() 26 | } 27 | } 28 | 29 | pub fn (mut mc ModuleCache) lookup_module_path_by_file(path string, import_name string) string { 30 | return mc.lookup_module_path_by_folder(os.dir(path), import_name) 31 | } 32 | 33 | pub fn (mut mc ModuleCache) lookup_module_path_by_folder(path string, import_name string) string { 34 | real_path := os.real_path(path) 35 | if real_path in mc.modules { 36 | return mc.modules[import_name] 37 | } 38 | import_path := get_module_path(import_name) 39 | // check local directory and current path first 40 | // and add the path to the folder of first found 41 | // v.mod file 42 | mut lookup_paths := [real_path, mc.current_dir, mc.lib_path] 43 | path_to_vmod := mc.lookup_v_mod_by_folder(real_path) 44 | if path_to_vmod.len > 0 { 45 | lookup_paths << path_to_vmod 46 | } 47 | for p in lookup_paths { 48 | mod_folder_path := os.join_path(p, import_path) 49 | if os.is_dir(mod_folder_path) { 50 | mc.modules[import_name] = mod_folder_path 51 | return mod_folder_path 52 | } 53 | } 54 | // traverse up using parents 55 | return '' 56 | } 57 | pub fn (mut mc ModuleCache) lookup_v_mod_by_folder(path string) string { 58 | $if windows { 59 | // windows root path is C: or D: 60 | if path.len <= 2 { 61 | return '' 62 | } 63 | } $else { 64 | if path.len == 0 { 65 | return '' 66 | } 67 | } 68 | if path in mc.vmod_paths { 69 | // the path is in the cache 70 | return mc.vmod_paths[path] 71 | } 72 | files := os.ls(path) or { 73 | []string{} 74 | } 75 | if 'v.mod' in files { 76 | mc.vmod_paths[path] = path 77 | return path 78 | } 79 | 80 | // no vmod files found so continue traversing parent 81 | parent_folder := os.dir(path) 82 | vmod_path := mc.lookup_v_mod_by_folder(parent_folder) 83 | 84 | if vmod_path.len > 0 { 85 | // add all parent 86 | mc.vmod_paths[path] = vmod_path 87 | return vmod_path 88 | } 89 | return vmod_path 90 | } 91 | 92 | // lookup_full_module_name finds the first occurrence of a v.mod file 93 | pub fn (mut mc ModuleCache) lookup_full_module_name(start_folder string, file_path string, module_name string) string { 94 | real_path := os.real_path(os.dir(file_path)) 95 | 96 | real_start_folder_name := os.real_path(start_folder) 97 | if real_path.starts_with(start_folder) { 98 | if real_start_folder_name.len >= real_path.len { 99 | return module_name 100 | } 101 | mod_path := real_path[real_start_folder_name.len+1..] 102 | return get_module_name_from_path(mod_path) 103 | } 104 | 105 | vmod_path := mc.lookup_v_mod_by_folder(real_path) 106 | 107 | if vmod_path.len > 1 { 108 | // we gound a v.mod file so this is the path for 109 | // calculating the module name 110 | if vmod_path.len >= real_path.len { 111 | return module_name 112 | } 113 | mod_path := real_path[vmod_path.len+1..] 114 | return get_module_name_from_path(mod_path) 115 | } 116 | 117 | 118 | return '' 119 | } 120 | 121 | pub const( 122 | mod_cache = new_mod_cache() 123 | ) 124 | 125 | pub fn get_mod_cache() &ModuleCache { 126 | return mod_cache 127 | } 128 | [inline] 129 | fn get_module_path(mod string) string { 130 | return mod.replace('.', os.path_separator) 131 | } 132 | 133 | [inline] 134 | fn get_module_name_from_path(mod_path string) string { 135 | return mod_path.replace(os.path_separator, '.') 136 | } -------------------------------------------------------------------------------- /lib/comp/util/mod/mod_lookup_test.v: -------------------------------------------------------------------------------- 1 | module mod 2 | import os 3 | fn test_lookup_import_path_by_vmod() { 4 | test_dir := os.dir('${@FILE}') 5 | expected_dir := os.join_path(test_dir, 'tests/vmod/lib/a') 6 | b_file := os.join_path(test_dir, 'tests/vmod/lib/b/b.v') 7 | 8 | mod_path := mod_cache.lookup_module_path_by_file(b_file, 'lib.a') 9 | assert mod_path == expected_dir 10 | } 11 | 12 | fn test_lookup_import_path_by_current_folder() { 13 | test_dir := os.dir('${@FILE}') 14 | a_dir := os.join_path(test_dir, 'tests/vmod/lib/x.v') 15 | expected := os.join_path(test_dir, 'tests/vmod/lib/b') 16 | 17 | mod_path := mod_cache.lookup_module_path_by_file(a_dir, 'b') 18 | assert mod_path == expected 19 | } 20 | 21 | fn test_lookup_import_path_by_lib() { 22 | test_dir := os.dir('${@FILE}') 23 | b_file := os.join_path(test_dir, 'tests/vmod/lib/b/b.v') 24 | expected := os.join_path(mod_cache.lib_path, 'comp/ast') 25 | 26 | mod_path := mod_cache.lookup_module_path_by_file(b_file, 'comp.ast') 27 | assert mod_path == expected 28 | } 29 | 30 | fn test_lookup_real_module_name_vmod() { 31 | test_dir := os.dir('${@FILE}') 32 | b_file := os.join_path(test_dir, 'tests/vmod/lib/b/b.v') 33 | compile_file := os.join_path(test_dir, 'tests/vmod/lib/a') 34 | real_mod := mod_cache.lookup_full_module_name(compile_file, b_file, 'b') 35 | assert real_mod == 'lib.b' 36 | } 37 | 38 | fn test_lookup_real_module_name_by_folder() { 39 | test_dir := os.dir('${@FILE}') 40 | a_file := os.join_path(test_dir, 'tests/nomod/a/a.v') 41 | compile_file := os.join_path(test_dir, 'tests/nomod') 42 | real_mod := mod_cache.lookup_full_module_name(compile_file, a_file, 'a') 43 | assert real_mod == 'a' 44 | } -------------------------------------------------------------------------------- /lib/comp/util/mod/tests/nomod/a/a.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helto4real/micro_v/7463285bdf75c299328282eebb7413eb4054d666/lib/comp/util/mod/tests/nomod/a/a.v -------------------------------------------------------------------------------- /lib/comp/util/mod/tests/vmod/lib/a/a.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helto4real/micro_v/7463285bdf75c299328282eebb7413eb4054d666/lib/comp/util/mod/tests/vmod/lib/a/a.v -------------------------------------------------------------------------------- /lib/comp/util/mod/tests/vmod/lib/b/b.v: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helto4real/micro_v/7463285bdf75c299328282eebb7413eb4054d666/lib/comp/util/mod/tests/vmod/lib/b/b.v -------------------------------------------------------------------------------- /lib/comp/util/mod/tests/vmod/v.mod: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/helto4real/micro_v/7463285bdf75c299328282eebb7413eb4054d666/lib/comp/util/mod/tests/vmod/v.mod -------------------------------------------------------------------------------- /lib/comp/util/pref/pref.v: -------------------------------------------------------------------------------- 1 | module pref 2 | 3 | // CompPref contains the compiler preferences 4 | pub struct CompPref { 5 | pub: 6 | is_prod bool // true to compile with optimizations 7 | print_ll bool // true to print the module after compile or run 8 | output string // path to output file 9 | } 10 | 11 | pub fn new_comp_pref(is_prod bool, print_ll bool, output string) CompPref { 12 | return CompPref{ 13 | is_prod: is_prod 14 | print_ll: print_ll 15 | output: output 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /lib/comp/util/source/diag_diagnostics.v: -------------------------------------------------------------------------------- 1 | module source 2 | 3 | pub struct Diagnostic { 4 | pub: 5 | has_loc bool 6 | location TextLocation // location of error 7 | text string // error text 8 | } 9 | 10 | pub struct Diagnostics { 11 | mut: 12 | iter_pos int 13 | pub mut: 14 | all []&Diagnostic 15 | } 16 | 17 | pub fn new_diagonistics() &Diagnostics { 18 | return &Diagnostics{} 19 | } 20 | 21 | pub fn (mut d Diagnostics) merge(from_diag &Diagnostics) { 22 | for diag in from_diag { 23 | d.all << diag 24 | } 25 | } 26 | 27 | // iterator for more easy handling 28 | pub fn (mut d Diagnostics) next() ?&Diagnostic { 29 | if d.iter_pos < d.all.len { 30 | ret := d.all[d.iter_pos] 31 | d.iter_pos++ 32 | return ret 33 | } 34 | d.iter_pos = 0 35 | return none 36 | } 37 | 38 | pub fn (mut d Diagnostics) error(text string, location TextLocation) { 39 | d.all << &Diagnostic{ 40 | text: text 41 | location: location 42 | has_loc: true 43 | } 44 | } 45 | 46 | pub fn (mut d Diagnostics) error_msg(text string) { 47 | d.all << &Diagnostic{ 48 | text: text 49 | has_loc: false 50 | } 51 | } -------------------------------------------------------------------------------- /lib/comp/util/source/diag_print_diag.v: -------------------------------------------------------------------------------- 1 | module source 2 | 3 | import strings 4 | import term 5 | 6 | pub fn write_diagnostic(mut sw SourceWriter, location &TextLocation, text string, nr_lines_to_show int) { 7 | source := location.source 8 | src := source.str() 9 | error_line_nr := source.line_nr(location.pos.pos) 10 | error_line := source.lines[error_line_nr - 1] 11 | error_col := location.pos.pos - error_line.start + 1 12 | 13 | mut line_nr_start := error_line_nr - nr_lines_to_show 14 | if line_nr_start < 1 { 15 | line_nr_start = 1 16 | } 17 | 18 | error_line_nr_end := source.line_nr(location.pos.pos + location.pos.len) 19 | mut line_nr_end := error_line_nr_end + nr_lines_to_show 20 | if line_nr_end > source.lines.len { 21 | line_nr_end = source.lines.len 22 | } 23 | 24 | mut err_end_pos := location.pos.pos + location.pos.len 25 | if err_end_pos > src.len { 26 | err_end_pos = src.len 27 | } 28 | 29 | sw.write('$location.source.filename:$error_line_nr:$error_col: ') 30 | sw.write(term.red('error: ')) 31 | sw.writeln(text) 32 | 33 | mut b := strings.new_builder(0) 34 | nr_of_digits := line_nr_end.str().len 35 | for i in line_nr_start .. line_nr_end + 1 { 36 | line := source.lines[i - 1] 37 | nr_of_zeros_to_add := nr_of_digits - i.str().len 38 | if nr_of_zeros_to_add > 0 { 39 | b.write_string(' 0'.repeat(nr_of_zeros_to_add)) 40 | } else { 41 | b.write_string(' ') 42 | } 43 | b.write_string('$i') 44 | b.write_string(' | ') 45 | if i == error_line_nr { 46 | prefix := src[line.start..location.pos.pos].replace('\t', ' ') 47 | error := src[location.pos.pos..err_end_pos].replace('\t', ' ') 48 | start := location.pos.pos + location.pos.len 49 | end := line.start + line.len 50 | postfix := if start < end && end < src.len { 51 | src[start..end].replace('\t', ' ') 52 | } else { 53 | '' 54 | } 55 | 56 | b.write_string(prefix) 57 | b.write_string(term.red(error)) 58 | b.writeln(postfix) 59 | b.write_string(' '.repeat(nr_of_digits + 1)) 60 | b.write_string(' | ') 61 | b.writeln(term.red('${' '.repeat(prefix.len)}${'~'.repeat(location.pos.len)}')) 62 | } else { 63 | b.writeln(src[line.start..line.start + line.len].replace('\t', ' ')) 64 | } 65 | } 66 | sw.writeln(b.str()) 67 | sw.writeln('') 68 | } 69 | -------------------------------------------------------------------------------- /lib/comp/util/source/diag_text_location.v: -------------------------------------------------------------------------------- 1 | module source 2 | 3 | struct TextLocation { 4 | pub: 5 | source &SourceText 6 | pos Pos 7 | } 8 | 9 | pub fn new_text_location(source &SourceText, pos Pos) TextLocation { 10 | return TextLocation{ 11 | source: source 12 | pos: pos 13 | } 14 | } 15 | 16 | pub fn (tl TextLocation) start_line() int { 17 | return tl.source.line_nr(tl.pos.pos) 18 | } 19 | 20 | pub fn (tl TextLocation) end_line() int { 21 | return tl.source.line_nr(tl.pos.pos + tl.pos.len) 22 | } 23 | 24 | pub fn (tl TextLocation) start_character() int { 25 | return tl.pos.pos - tl.source.lines[tl.start_line() - 1].start 26 | } 27 | 28 | pub fn (tl TextLocation) end_character() int { 29 | return tl.pos.pos + tl.pos.len - tl.source.lines[tl.start_line() - 1].start 30 | } 31 | -------------------------------------------------------------------------------- /lib/comp/util/source/io.v: -------------------------------------------------------------------------------- 1 | module source 2 | 3 | import term 4 | import strings 5 | 6 | pub struct SourceWriter { 7 | mut: 8 | current_indent int 9 | builder strings.Builder = strings.new_builder(0) 10 | write_indent bool = true 11 | } 12 | 13 | pub fn (mut i SourceWriter) str() string { 14 | return i.builder.str() 15 | } 16 | 17 | pub fn (mut i SourceWriter) indent() int { 18 | return i.current_indent 19 | } 20 | 21 | pub fn (mut i SourceWriter) write_keyword(s string) { 22 | i.write_indent() 23 | i.builder.write_string(term.magenta(s)) 24 | } 25 | 26 | pub fn (mut i SourceWriter) write_comment(s string) { 27 | i.write_indent() 28 | i.builder.write_string(term.green(s)) 29 | } 30 | 31 | pub fn (mut i SourceWriter) write_string(s string) { 32 | i.write_indent() 33 | i.builder.write_string(term.bright_green(s)) 34 | } 35 | 36 | pub fn (mut i SourceWriter) write_number(s string) { 37 | i.write_indent() 38 | i.builder.write_string(term.rgb(146, 140, 0, s)) 39 | } 40 | 41 | pub fn (mut i SourceWriter) write_identifier(s string) { 42 | i.write_indent() 43 | i.builder.write_string(term.bright_blue(s)) 44 | } 45 | 46 | pub fn (mut i SourceWriter) write_punctuation(s string) { 47 | i.write_indent() 48 | i.builder.write_string(term.gray(s)) 49 | } 50 | 51 | pub fn (mut i SourceWriter) write_space() { 52 | i.write_indent() 53 | i.builder.write_string(' ') 54 | } 55 | 56 | pub fn (mut i SourceWriter) writeln(s string) { 57 | i.write_indent() 58 | i.builder.writeln(s) 59 | i.write_indent = true 60 | } 61 | 62 | pub fn (mut i SourceWriter) write(s string) { 63 | i.write_indent() 64 | i.builder.write_string(s) 65 | } 66 | 67 | pub fn (mut i SourceWriter) indent_add(n int) { 68 | i.current_indent = i.current_indent + n 69 | } 70 | 71 | [inline] 72 | fn (mut i SourceWriter) write_indent() { 73 | if i.write_indent && i.current_indent > 0 { 74 | i.builder.write_string(' '.repeat(i.current_indent)) 75 | i.write_indent = false 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /lib/comp/util/source/pos.v: -------------------------------------------------------------------------------- 1 | module source 2 | 3 | pub struct Pos { 4 | pub: 5 | pos int // position in textfile 6 | len int // length of the error token 7 | } 8 | 9 | pub fn new_pos(pos int, len int) Pos { 10 | return Pos{ 11 | pos: pos 12 | len: len 13 | } 14 | } 15 | 16 | pub fn new_pos_from_pos_bounds(start Pos, end Pos) Pos { 17 | return Pos{ 18 | pos: start.pos 19 | len: end.pos - start.pos + end.len 20 | } 21 | } 22 | 23 | pub fn new_pos_from_bounds(start int, end int) Pos { 24 | return Pos{ 25 | pos: start 26 | len: end - start 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /lib/comp/util/source/source_code_test.v: -------------------------------------------------------------------------------- 1 | import lib.comp.util.source 2 | 3 | fn test_source_at() { 4 | source := source.new_source_text('abc') 5 | 6 | assert source.at(0) == `a` 7 | assert source.at(1) == `b` 8 | assert source.at(2) == `c` 9 | assert source.at(3) == `\0` 10 | assert source.at(4) == `\0` 11 | } 12 | 13 | fn test_source_range() { 14 | source := source.new_source_text('abc123') 15 | 16 | assert source.str_range(0, 2) == 'ab' 17 | assert source.str_range(3, 4) == '1' 18 | } 19 | 20 | fn text_line_parsing() { 21 | mut source := source.new_source_text('abc123\n321abc') 22 | 23 | source.add_line(0, 5, 1) 24 | 25 | assert source.lines.len == 2 26 | assert source.lines[0].str() == 'abc123' 27 | assert source.lines[1].str() == '321abc' 28 | } 29 | 30 | fn text_line_parsing_crln() { 31 | mut source := source.new_source_text('abc123\r\n321abc') 32 | 33 | source.add_line(0, 5, 2) 34 | 35 | assert source.lines.len == 2 36 | assert source.lines[0].str() == 'abc123' 37 | assert source.lines[1].str() == '321abc' 38 | } 39 | 40 | fn text_empty_line_parsing() { 41 | mut source := source.new_source_text('abc123\n') 42 | 43 | source.add_line(0, 5, 1) 44 | 45 | assert source.lines.len == 2 46 | assert source.lines[0].str() == 'abc123' 47 | assert source.lines[1].str() == '' 48 | } 49 | 50 | fn text_line_pos() { 51 | mut source := source.new_source_text('abc123\n321abc') 52 | 53 | source.add_line(0, 5, 1) 54 | 55 | assert source.lines.len == 2 56 | assert source.lines[0].pos().pos == 0 57 | assert source.lines[0].pos().len == 3 58 | assert source.lines[1].pos().pos == 6 59 | assert source.lines[1].pos().len == 3 60 | } 61 | 62 | fn test_line_pos_crln() { 63 | mut source := source.new_source_text('abc123\r\n321abc') 64 | 65 | source.add_line(0, 5, 2) 66 | source.add_line(8, 13, 0) 67 | 68 | assert source.lines.len == 2 69 | assert source.lines[0].pos().pos == 0 70 | assert source.lines[0].pos().len == 6 71 | assert source.lines[1].pos().pos == 8 72 | assert source.lines[1].pos().len == 6 73 | } 74 | 75 | fn test_text_line_nr() { 76 | mut source := source.new_source_text('abc123\r\n321abc\r\n123456') 77 | 78 | source.add_line(0, 5, 2) 79 | source.add_line(8, 13, 2) 80 | source.add_line(16, 21, 0) 81 | 82 | assert source.lines.len == 3 83 | assert source.line_nr(0) == 1 84 | assert source.line_nr(1) == 1 85 | assert source.line_nr(2) == 1 86 | assert source.line_nr(3) == 1 87 | assert source.line_nr(4) == 1 88 | assert source.line_nr(5) == 1 89 | assert source.line_nr(6) == 1 90 | assert source.line_nr(7) == 1 91 | assert source.line_nr(8) == 2 92 | assert source.line_nr(9) == 2 93 | assert source.line_nr(10) == 2 94 | assert source.line_nr(11) == 2 95 | assert source.line_nr(12) == 2 96 | assert source.line_nr(14) == 2 97 | assert source.line_nr(15) == 2 98 | assert source.line_nr(16) == 3 99 | assert source.line_nr(18) == 3 100 | assert source.line_nr(19) == 3 101 | assert source.line_nr(20) == 3 102 | assert source.line_nr(21) == 3 103 | assert source.line_nr(22) == 3 104 | } 105 | -------------------------------------------------------------------------------- /lib/comp/util/source/source_text.v: -------------------------------------------------------------------------------- 1 | module source 2 | 3 | import lib.comp.util.source 4 | 5 | // SourceCode handles the source handling features 6 | // - linenumber and columns 7 | // - formatting of errors 8 | pub struct SourceText { 9 | text string 10 | pub: 11 | filename string 12 | pub mut: 13 | lines []TextLine 14 | } 15 | 16 | pub fn new_source_text_from_file(text string, filename string) &SourceText { 17 | return &source.SourceText{ 18 | text: text 19 | filename: filename 20 | } 21 | } 22 | 23 | pub fn new_source_text(text string) &SourceText { 24 | return &source.SourceText{ 25 | text: text 26 | } 27 | } 28 | 29 | // line_nr, returns line number for current positio 30 | pub fn (s SourceText) line_nr(pos int) int { 31 | // use binary search for line number 32 | mut lower := 0 33 | mut upper := s.lines.len - 1 34 | for lower <= upper { 35 | index := lower + (upper - lower) / 2 36 | start := s.lines[index].start 37 | if pos == start { 38 | return index + 1 39 | } 40 | if start > pos { 41 | upper = index - 1 42 | } else { 43 | lower = index + 1 44 | } 45 | } 46 | return lower 47 | } 48 | 49 | pub fn (s &SourceText) str() string { 50 | return s.text 51 | } 52 | 53 | [inline] 54 | pub fn (s &SourceText) str_range(start int, end int) string { 55 | return s.text[start..end] 56 | } 57 | 58 | [inline] 59 | pub fn (s &SourceText) str_pos(pos Pos) string { 60 | return s.text[pos.pos..pos.pos + pos.len] 61 | } 62 | 63 | pub fn (mut s SourceText) add_line(start_pos int, end_pos int, lb_len int) { 64 | s.lines << new_text_line(s, start_pos, end_pos - start_pos + 1, lb_len) 65 | } 66 | 67 | [inline] 68 | pub fn (s &SourceText) at(pos int) byte { 69 | return if pos < s.text.len { s.text[pos] } else { `\0` } 70 | } 71 | 72 | pub struct TextLine { 73 | source &SourceText 74 | pub: 75 | start int 76 | len int 77 | lb_len int 78 | } 79 | 80 | fn new_text_line(source_text &SourceText, start int, len int, lb_len int) TextLine { 81 | return source.TextLine{ 82 | source: source_text 83 | start: start 84 | len: len 85 | lb_len: lb_len 86 | } 87 | } 88 | 89 | pub fn (tl TextLine) pos() Pos { 90 | return source.new_pos(tl.start, tl.len) 91 | } 92 | 93 | pub fn (tl TextLine) pos_include_linebreak() Pos { 94 | return source.new_pos(tl.start, tl.len + tl.lb_len) 95 | } 96 | 97 | pub fn (tl TextLine) str() string { 98 | return tl.source.str_range(tl.start, tl.start + tl.len) 99 | } 100 | -------------------------------------------------------------------------------- /lib/comp/util/stack.v: -------------------------------------------------------------------------------- 1 | module util 2 | 3 | struct Stack { 4 | mut: 5 | size int 6 | elements []voidptr 7 | } 8 | 9 | pub fn new_stack() Stack { 10 | return Stack{} 11 | } 12 | 13 | [inline] 14 | pub fn (stack Stack) is_empty() bool { 15 | return stack.size <= 0 16 | } 17 | 18 | pub fn (stack Stack) peek() ?voidptr { 19 | if !stack.is_empty() { 20 | return stack.elements[stack.size - 1] 21 | } else { 22 | return none 23 | } 24 | } 25 | 26 | pub fn (mut stack Stack) pop() ?voidptr { 27 | if !stack.is_empty() { 28 | val := stack.elements[stack.size - 1] 29 | stack.size-- 30 | return val 31 | } 32 | return none 33 | } 34 | 35 | pub fn (mut stack Stack) push(item voidptr) { 36 | if stack.elements.len > stack.size { 37 | stack.elements[stack.size] = item 38 | } else { 39 | stack.elements << item 40 | } 41 | stack.size++ 42 | } 43 | -------------------------------------------------------------------------------- /lib/comp/util/stack_test.v: -------------------------------------------------------------------------------- 1 | import lib.comp.util 2 | 3 | [heap] 4 | struct Node { 5 | val int 6 | } 7 | 8 | struct NodeStack { 9 | mut: 10 | stack util.Stack 11 | } 12 | 13 | fn new_node_stack() NodeStack { 14 | return NodeStack{} 15 | } 16 | 17 | fn new_node(val int) &Node { 18 | return &Node{ 19 | val: val 20 | } 21 | } 22 | 23 | fn (mut ns NodeStack) push(val int) { 24 | ns.stack.push(new_node(val)) 25 | } 26 | 27 | fn (mut ns NodeStack) pop() ?&Node { 28 | val := ns.stack.pop() or { return none } 29 | return &Node(val) 30 | } 31 | 32 | fn (ns NodeStack) peek() ?&Node { 33 | val := ns.stack.peek() or { return none } 34 | return &Node(val) 35 | } 36 | 37 | [inline] 38 | fn (ns NodeStack) is_empty() bool { 39 | return ns.stack.is_empty() 40 | } 41 | 42 | fn test_basic_stack_operations() { 43 | mut stack := new_node_stack() 44 | 45 | stack.push(100) 46 | 47 | assert stack.is_empty() == false 48 | 49 | pop_val := stack.pop() or { &Node(0) } 50 | 51 | assert pop_val != 0 52 | assert pop_val.val == 100 53 | 54 | stack.push(1) 55 | stack.push(2) 56 | stack.push(3) 57 | stack.push(4) 58 | stack.push(5) 59 | stack.push(6) 60 | stack.push(7) 61 | 62 | mut node := stack.pop() or { panic('') } 63 | assert node.val == 7 64 | 65 | stack.push(8) 66 | node = stack.pop() or { panic('') } 67 | assert node.val == 8 68 | node = stack.pop() or { panic('') } 69 | assert node.val == 6 70 | node = stack.pop() or { panic('') } 71 | assert node.val == 5 72 | node = stack.pop() or { panic('') } 73 | assert node.val == 4 74 | node = stack.pop() or { panic('') } 75 | assert node.val == 3 76 | node = stack.pop() or { panic('') } 77 | assert node.val == 2 78 | node = stack.pop() or { panic('') } 79 | assert node.val == 1 80 | node = stack.pop() or { &Node(0) } 81 | assert !(node != 0) 82 | } 83 | 84 | fn test_basic_stack_empty_stack() { 85 | mut stack := new_node_stack() 86 | 87 | assert stack.is_empty() == true 88 | pop_val := stack.pop() or { &Node(0) } 89 | if pop_val != 0 { 90 | assert false 91 | } 92 | 93 | stack.push(100) 94 | real_val := stack.pop() or { &Node(0) } 95 | assert real_val != 0 96 | 97 | assert stack.is_empty() == true 98 | pop_val_a := stack.pop() or { &Node(0) } 99 | assert !(pop_val_a != 0) 100 | } 101 | -------------------------------------------------------------------------------- /lib/repl/io.v: -------------------------------------------------------------------------------- 1 | module repl 2 | 3 | import term 4 | import strings 5 | import lib.comp.binding 6 | // import lib.co'mp.util 7 | // import lib.comp.parser 8 | 9 | pub struct IdentWriter { 10 | mut: 11 | current_indent int 12 | builder strings.Builder = strings.new_builder(0) 13 | write_indent bool = true 14 | } 15 | 16 | pub fn get_bound_node_string(node binding.BoundNode) string { 17 | mut iw := IdentWriter{} 18 | binding.write_node(iw, node) 19 | return iw.builder.str() 20 | } 21 | 22 | pub fn (mut i IdentWriter) str() string { 23 | return i.builder.str() 24 | } 25 | 26 | pub fn (mut i IdentWriter) indent() int { 27 | return i.current_indent 28 | } 29 | 30 | pub fn (mut i IdentWriter) write_keyword(s string) { 31 | i.write_indent() 32 | i.builder.write_string(term.magenta(s)) 33 | } 34 | 35 | pub fn (mut i IdentWriter) write_comment(s string) { 36 | i.write_indent() 37 | i.builder.write_string(term.green(s)) 38 | } 39 | 40 | pub fn (mut i IdentWriter) write_string(s string) { 41 | i.write_indent() 42 | i.builder.write_string(term.bright_green(s)) 43 | } 44 | 45 | pub fn (mut i IdentWriter) write_number(s string) { 46 | i.write_indent() 47 | i.builder.write_string(term.rgb(146, 140, 0, s)) 48 | } 49 | 50 | pub fn (mut i IdentWriter) write_identifier(s string) { 51 | i.write_indent() 52 | i.builder.write_string(term.bright_blue(s)) 53 | } 54 | 55 | pub fn (mut i IdentWriter) write_punctuation(s string) { 56 | i.write_indent() 57 | i.builder.write_string(term.gray(s)) 58 | } 59 | 60 | pub fn (mut i IdentWriter) write_space() { 61 | i.write_indent() 62 | i.builder.write_string(' ') 63 | } 64 | 65 | pub fn (mut i IdentWriter) writeln(s string) { 66 | i.write_indent() 67 | i.builder.writeln(s) 68 | i.write_indent = true 69 | } 70 | 71 | pub fn (mut i IdentWriter) write(s string) { 72 | i.write_indent() 73 | i.builder.write_string(s) 74 | } 75 | 76 | pub fn (mut i IdentWriter) indent_add(n int) { 77 | i.current_indent = i.current_indent + n 78 | } 79 | 80 | // pub fn (mut i IdentWriter) write_diagnostics(diagnostics []&source.Diagnostic, syntax_tree ast.SyntaxTree) { 81 | // // for err in diagnostics { 82 | // // src := syntax_tree.source.str() 83 | // // line_nr := syntax_tree.source.line_nr(err.pos.pos) 84 | // // prefix := src[0..err.pos.pos] 85 | // // mut err_end_pos := err.pos.pos + err.pos.len 86 | // // if err_end_pos > src.len { 87 | // // err_end_pos = src.len 88 | // // } 89 | // // error := src[err.pos.pos..err_end_pos] 90 | 91 | // // postfix := if err_end_pos + err.pos.len < src.len { 92 | // // src[err.pos.pos + err.pos.len..] 93 | // // } else { 94 | // // '' 95 | // // } 96 | 97 | // // i.writeln(term.red(err.text)) 98 | // // i.writeln('') 99 | // // i.write_string('$line_nr| ') 100 | // // i.write_string(prefix.trim('\r\n')) 101 | // // i.write_string(term.red(error)) 102 | // // i.writeln(postfix) 103 | // // i.writeln('') 104 | // // } 105 | // } 106 | 107 | [inline] 108 | fn (mut i IdentWriter) write_indent() { 109 | if i.write_indent && i.current_indent > 0 { 110 | i.builder.write_string(' '.repeat(i.current_indent)) 111 | i.write_indent = false 112 | } 113 | } 114 | -------------------------------------------------------------------------------- /lib/repl/ui.v: -------------------------------------------------------------------------------- 1 | module repl 2 | 3 | enum Movement { 4 | up 5 | down 6 | left 7 | right 8 | home 9 | end 10 | page_up 11 | page_down 12 | } 13 | 14 | struct View { 15 | pub: 16 | raw string 17 | cursor Cursor 18 | } 19 | 20 | struct Cursor { 21 | pub mut: 22 | pos_x int 23 | pos_y int 24 | } 25 | 26 | fn (mut c Cursor) set(x int, y int) { 27 | c.pos_x = x 28 | c.pos_y = y 29 | } 30 | 31 | fn (mut c Cursor) move(x int, y int) { 32 | c.pos_x += x 33 | c.pos_y += y 34 | } 35 | 36 | fn (c Cursor) xy() (int, int) { 37 | return c.pos_x, c.pos_y 38 | } 39 | -------------------------------------------------------------------------------- /lib/runtime/standard.c.v: -------------------------------------------------------------------------------- 1 | module runtime 2 | 3 | fn C.printf(fmt &byte, params ...&byte) int 4 | 5 | fn C.exit(code int) 6 | 7 | fn C.sprintf(buffer &byte, fmt &byte, params ...&byte) int 8 | 9 | fn C.strlen(str &char) int 10 | 11 | struct C.JumpBuffer { 12 | var i64 13 | } 14 | fn C.longjmp(b &C.JumpBuffer, val i64) 15 | fn C.setjmp(b &C.JumpBuffer) i64 16 | // declare void @longjmp(%JumpBuffer*, i64) 17 | 18 | // declare i64 @setjmp(%JumpBuffer*) -------------------------------------------------------------------------------- /lib/runtime/string.v: -------------------------------------------------------------------------------- 1 | 2 | module runtime 3 | struct String { 4 | str &char 5 | len int 6 | 7 | is_lit int 8 | } 9 | 10 | fn vstrlen(s &byte) int { 11 | return C.strlen(&char(s)) 12 | } -------------------------------------------------------------------------------- /samples/.gitignore: -------------------------------------------------------------------------------- 1 | *.go 2 | *.s 3 | *.ll 4 | -------------------------------------------------------------------------------- /samples/arrays/arrays.v: -------------------------------------------------------------------------------- 1 | fn main () { 2 | arr := [1,2,3,4]! 3 | 4 | for x in 0..4 { 5 | println(string(arr[x])) 6 | } 7 | } 8 | -------------------------------------------------------------------------------- /samples/c_interop/time.v: -------------------------------------------------------------------------------- 1 | 2 | struct Time { 3 | year int 4 | month int 5 | day int 6 | hour int 7 | minute int 8 | second int 9 | microsecond int 10 | unix i64 11 | } 12 | struct C.tm { 13 | tm_sec int 14 | tm_min int 15 | tm_hour int 16 | tm_mday int 17 | tm_mon int 18 | tm_year int 19 | tm_wday int 20 | tm_yday int 21 | tm_isdst int 22 | } 23 | 24 | struct C.timespec { 25 | tv_sec i64 26 | tv_nsec i64 27 | } 28 | 29 | fn C.time(t &i64) i64 30 | 31 | fn C.localtime(t &i64) &C.tm 32 | 33 | fn main() { 34 | time_t := i64(0) 35 | C.time(&time_t) 36 | t := C.localtime(&time_t) 37 | println(string(t.tm_year)) 38 | 39 | } -------------------------------------------------------------------------------- /samples/hello/hello_world.s: -------------------------------------------------------------------------------- 1 | .text 2 | .file "program" 3 | .globl main # -- Begin function main 4 | .p2align 4, 0x90 5 | .type main,@function 6 | main: # @main 7 | .cfi_startproc 8 | # %bb.0: # %entry 9 | pushq %rbp 10 | .cfi_def_cfa_offset 16 11 | .cfi_offset %rbp, -16 12 | movq %rsp, %rbp 13 | .cfi_def_cfa_register %rbp 14 | subq $16, %rsp 15 | movabsq $jmp_buf, %rdi 16 | callq setjmp 17 | cmpq $0, %rax 18 | jne .LBB0_4 19 | # %bb.1: # %continue 20 | movq %rsp, %rax 21 | addq $-16, %rax 22 | movq %rax, %rsp 23 | movl $100, (%rax) 24 | movq %rax, %rdi 25 | movq %rax, -8(%rbp) # 8-byte Spill 26 | callq mutable_function_param_with_assert 27 | movq -8(%rbp), %rax # 8-byte Reload 28 | cmpl $10, (%rax) 29 | je .LBB0_3 30 | # %bb.2: # %assert 31 | movabsq $.L__unnamed_1, %rdi 32 | movabsq $.L__unnamed_2, %rsi 33 | movb $0, %al 34 | callq printf 35 | movabsq $jmp_buf, %rdi 36 | movl $1, %esi 37 | movl %eax, -12(%rbp) # 4-byte Spill 38 | callq longjmp 39 | .LBB0_3: # %assert_cont 40 | xorl %eax, %eax 41 | movq %rbp, %rsp 42 | popq %rbp 43 | .cfi_def_cfa %rsp, 8 44 | retq 45 | .LBB0_4: # %error_exit 46 | .cfi_def_cfa %rbp, 16 47 | movl $1, %eax 48 | movq %rbp, %rsp 49 | popq %rbp 50 | .cfi_def_cfa %rsp, 8 51 | retq 52 | .Lfunc_end0: 53 | .size main, .Lfunc_end0-main 54 | .cfi_endproc 55 | # -- End function 56 | .globl no_mut_or_ref # -- Begin function no_mut_or_ref 57 | .p2align 4, 0x90 58 | .type no_mut_or_ref,@function 59 | no_mut_or_ref: # @no_mut_or_ref 60 | .cfi_startproc 61 | # %bb.0: # %entry 62 | pushq %rax 63 | .cfi_def_cfa_offset 16 64 | movl %edi, 4(%rsp) 65 | cmpl $10, 4(%rsp) 66 | je .LBB1_2 67 | # %bb.1: # %assert 68 | movabsq $.L__unnamed_1, %rdi 69 | movabsq $.L__unnamed_3, %rsi 70 | movb $0, %al 71 | callq printf 72 | movabsq $jmp_buf, %rdi 73 | movl $1, %esi 74 | movl %eax, (%rsp) # 4-byte Spill 75 | callq longjmp 76 | .LBB1_2: # %assert_cont 77 | popq %rax 78 | .cfi_def_cfa_offset 8 79 | retq 80 | .Lfunc_end1: 81 | .size no_mut_or_ref, .Lfunc_end1-no_mut_or_ref 82 | .cfi_endproc 83 | # -- End function 84 | .globl mutable_function_param_with_assert # -- Begin function mutable_function_param_with_assert 85 | .p2align 4, 0x90 86 | .type mutable_function_param_with_assert,@function 87 | mutable_function_param_with_assert: # @mutable_function_param_with_assert 88 | .cfi_startproc 89 | # %bb.0: # %entry 90 | pushq %rax 91 | .cfi_def_cfa_offset 16 92 | movl (%rdi), %edi 93 | callq no_mut_or_ref 94 | popq %rax 95 | .cfi_def_cfa_offset 8 96 | retq 97 | .Lfunc_end2: 98 | .size mutable_function_param_with_assert, .Lfunc_end2-mutable_function_param_with_assert 99 | .cfi_endproc 100 | # -- End function 101 | .globl vstrlen # -- Begin function vstrlen 102 | .p2align 4, 0x90 103 | .type vstrlen,@function 104 | vstrlen: # @vstrlen 105 | .cfi_startproc 106 | # %bb.0: # %entry 107 | pushq %rax 108 | .cfi_def_cfa_offset 16 109 | callq strlen 110 | popq %rcx 111 | .cfi_def_cfa_offset 8 112 | retq 113 | .Lfunc_end3: 114 | .size vstrlen, .Lfunc_end3-vstrlen 115 | .cfi_endproc 116 | # -- End function 117 | .type sprintf_buff,@object # @sprintf_buff 118 | .bss 119 | .globl sprintf_buff 120 | .p2align 4 121 | sprintf_buff: 122 | .zero 21 123 | .size sprintf_buff, 21 124 | 125 | .type jmp_buf,@object # @jmp_buf 126 | .globl jmp_buf 127 | .p2align 3 128 | jmp_buf: 129 | .zero 8 130 | .size jmp_buf, 8 131 | 132 | .type .L__unnamed_2,@object # @0 133 | .section .rodata.str1.1,"aMS",@progbits,1 134 | .L__unnamed_2: 135 | .asciz "samples/hello/hello_world.v:16:2: \033[31merror: \033[39massert error\n 15 | mutable_function_param_with_assert(mut x)\n 16 | \033[31massert x == 10\033[39m\n | \033[31m ~~~~~~~~~~~~~~\033[39m\n 17 | }\n\n\n" 136 | .size .L__unnamed_2, 191 137 | 138 | .type .L__unnamed_1,@object # @1 139 | .L__unnamed_1: 140 | .asciz "%s\n" 141 | .size .L__unnamed_1, 4 142 | 143 | .type .L__unnamed_3,@object # @2 144 | .L__unnamed_3: 145 | .asciz "samples/hello/hello_world.v:6:2: \033[31merror: \033[39massert error\n 5 | fn no_mut_or_ref(x int) {\n 6 | \033[31massert x == 10\033[39m\n | \033[31m ~~~~~~~~~~~~~~\033[39m\n 7 | }\n\n\n" 146 | .size .L__unnamed_3, 168 147 | 148 | .section ".note.GNU-stack","",@progbits 149 | -------------------------------------------------------------------------------- /samples/hello/hello_world.v: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println('hello world!') 3 | } 4 | -------------------------------------------------------------------------------- /samples/loops/for_loop.v: -------------------------------------------------------------------------------- 1 | fn multi_print(name string) { 2 | for i in 0 .. 1000000 { 3 | // println(name) 4 | } 5 | } 6 | 7 | fn main() { 8 | multi_print('hello world') 9 | } 10 | -------------------------------------------------------------------------------- /samples/main_fn/main_fn.s: -------------------------------------------------------------------------------- 1 | .text 2 | .file "program" 3 | .globl main # -- Begin function main 4 | .p2align 4, 0x90 5 | .type main,@function 6 | main: # @main 7 | .cfi_startproc 8 | # %bb.0: # %entry 9 | pushq %rax 10 | .cfi_def_cfa_offset 16 11 | movl $jmp_buf, %edi 12 | callq setjmp 13 | testq %rax, %rax 14 | je .LBB0_2 15 | # %bb.1: # %error_exit 16 | movl $1, %eax 17 | popq %rcx 18 | .cfi_def_cfa_offset 8 19 | retq 20 | .LBB0_2: # %continue 21 | .cfi_def_cfa_offset 16 22 | movl $.L__unnamed_1, %edi 23 | movl $.L__unnamed_2, %esi 24 | xorl %eax, %eax 25 | callq printf 26 | movl $jmp_buf, %edi 27 | movl $1, %esi 28 | callq longjmp 29 | .Lfunc_end0: 30 | .size main, .Lfunc_end0-main 31 | .cfi_endproc 32 | # -- End function 33 | .type sprintf_buff,@object # @sprintf_buff 34 | .bss 35 | .globl sprintf_buff 36 | .p2align 4 37 | sprintf_buff: 38 | .zero 21 39 | .size sprintf_buff, 21 40 | 41 | .type jmp_buf,@object # @jmp_buf 42 | .globl jmp_buf 43 | .p2align 3 44 | jmp_buf: 45 | .zero 8 46 | .size jmp_buf, 8 47 | 48 | .type .L__unnamed_2,@object # @0 49 | .section .rodata.str1.1,"aMS",@progbits,1 50 | .L__unnamed_2: 51 | .asciz "fail on false" 52 | .size .L__unnamed_2, 14 53 | 54 | .type .L__unnamed_1,@object # @1 55 | .L__unnamed_1: 56 | .asciz "%s\n" 57 | .size .L__unnamed_1, 4 58 | 59 | .section ".note.GNU-stack","",@progbits 60 | -------------------------------------------------------------------------------- /samples/main_fn/main_fn.v: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println('hello world') 3 | } -------------------------------------------------------------------------------- /samples/module/module.v: -------------------------------------------------------------------------------- 1 | import my_mod 2 | 3 | fn main() { 4 | a := my_mod.a_cool_module(10) 5 | println(string(a)) 6 | 7 | t := my_mod.TestStruct { 8 | my_int: 10 9 | } 10 | t.a_func_on_test_struct() 11 | } -------------------------------------------------------------------------------- /samples/module/my_mod/my_mod.v: -------------------------------------------------------------------------------- 1 | module my_mod 2 | 3 | struct TestStruct { 4 | my_int int 5 | } 6 | 7 | fn (t TestStruct) a_func_on_test_struct() { 8 | println('hello') 9 | } 10 | 11 | fn a_cool_module(i int) int { 12 | return i 13 | } 14 | 15 | -------------------------------------------------------------------------------- /samples/mult_files/greet.v: -------------------------------------------------------------------------------- 1 | fn greet(name string) string { 2 | return 'Hello ' + name 3 | } -------------------------------------------------------------------------------- /samples/mult_files/main.v: -------------------------------------------------------------------------------- 1 | fn main() { 2 | println(greet('Tomas')) 3 | } -------------------------------------------------------------------------------- /samples/references/main.v: -------------------------------------------------------------------------------- 1 | // struct MyStruct { 2 | // an_int int 3 | // x string 4 | // } 5 | 6 | fn test(mut x int) { 7 | x = 10 8 | } 9 | 10 | // fn edit_struct(mut mss MyStruct) { 11 | // // a:= string( mss.an_int ) 12 | // // println('tomas') 13 | // // println(mss.x) 14 | // mss.x = 'another string' 15 | // } 16 | 17 | 18 | fn main() { 19 | // mut ms := MyStruct{ 20 | // an_int: 100 21 | // x: 'hello baby' 22 | // } 23 | // edit_struct(mut ms) 24 | // println(ms.x) 25 | mut a := 1 26 | test(mut a) 27 | println(string(a)) 28 | } -------------------------------------------------------------------------------- /samples/strings/strings.v: -------------------------------------------------------------------------------- 1 | 2 | fn main() { 3 | x := 'hello world' 4 | println(x) 5 | } -------------------------------------------------------------------------------- /samples/struct_fn/struct_fn.v: -------------------------------------------------------------------------------- 1 | struct TestStruct { 2 | an_int int 3 | } 4 | 5 | fn (t TestStruct) add_to_int(n int) int { 6 | // t.an_int = 5 7 | return t.an_int + n 8 | } 9 | 10 | fn main() { 11 | ts := TestStruct{ 12 | an_int: 10 13 | } 14 | 15 | a := ts.add_to_int(5) 16 | println(string(a)) 17 | } 18 | -------------------------------------------------------------------------------- /samples/structs/structs.v: -------------------------------------------------------------------------------- 1 | struct AnotherTestStruct { 2 | a_int int 3 | } 4 | 5 | fn (t AnotherTestStruct) sum(n int) int { 6 | return t.a_int + n 7 | } 8 | 9 | fn (t &AnotherTestStruct) ref_sum(n int) int { 10 | return t.a_int + n 11 | } 12 | 13 | fn (t AnotherTestStruct) sum_ref_param(n &int) int { 14 | return t.a_int + n 15 | } 16 | 17 | fn (t &AnotherTestStruct) ref_sum_ref_param(n &int) int { 18 | return t.a_int + n 19 | } 20 | 21 | fn (mut t AnotherTestStruct) add(n int) { 22 | t.a_int = t.a_int + n 23 | } 24 | 25 | fn main() { 26 | ts := AnotherTestStruct{ 27 | a_int: 100 28 | } 29 | res := ts.sum(10) 30 | println(string(res)) 31 | 32 | res2 := ts.ref_sum(20) 33 | println(string(res2)) 34 | 35 | val := 30 36 | res3 := ts.sum_ref_param(&val) 37 | println(string(res3)) 38 | 39 | val2 := 40 40 | res4 := ts.ref_sum_ref_param(&val2) 41 | println(string(res4)) 42 | 43 | mut mts := AnotherTestStruct{ 44 | a_int: 100 45 | } 46 | mts.add(100) 47 | println(string(mts.a_int)) 48 | } -------------------------------------------------------------------------------- /tests/c_interop/test_time.v: -------------------------------------------------------------------------------- 1 | 2 | struct Time { 3 | year int 4 | month int 5 | day int 6 | hour int 7 | minute int 8 | second int 9 | microsecond int 10 | unix i64 11 | } 12 | struct C.tm { 13 | tm_sec int 14 | tm_min int 15 | tm_hour int 16 | tm_mday int 17 | tm_mon int 18 | tm_year int 19 | tm_wday int 20 | tm_yday int 21 | tm_isdst int 22 | } 23 | 24 | struct C.timespec { 25 | tv_sec i64 26 | tv_nsec i64 27 | } 28 | 29 | fn C.time(t &i64) i64 30 | 31 | fn C.localtime(t &i64) &C.tm 32 | 33 | fn test_time() { 34 | time_t := i64(0) 35 | C.time(&time_t) 36 | t := C.localtime(&time_t) 37 | assert t.tm_year > 120 38 | 39 | } -------------------------------------------------------------------------------- /tests/operators/convertion_test.v: -------------------------------------------------------------------------------- 1 | fn test_basic_conversions() { 2 | assert 1==1 // fake test 3 | } 4 | -------------------------------------------------------------------------------- /tests/operators/operator_test.v: -------------------------------------------------------------------------------- 1 | fn test_basic_operators() { 2 | assert 1 + 1 == 2 3 | assert 10 * 11 == 110 4 | assert (50 + 50) * 10 == 1000 5 | assert 10 / 2 == 5 6 | assert 3 / 2 == 1 7 | } 8 | 9 | fn test_basic_logical_operators() { 10 | assert true == true 11 | assert false == false 12 | assert !false == true 13 | assert !true == false 14 | assert !(1 == 1) == false 15 | } 16 | -------------------------------------------------------------------------------- /tests/strings/strings_test.v: -------------------------------------------------------------------------------- 1 | fn test_assign_literal() { 2 | s := '12345' 3 | assert s.len == 5 4 | } 5 | fn test_assign_string() { 6 | s := '12345' 7 | a := s 8 | assert a.len == s.len 9 | } 10 | 11 | fn test_assign_string_and_change_it() { 12 | s := '12345' 13 | mut a := s 14 | a = '123456' 15 | assert a.len == 6 16 | } -------------------------------------------------------------------------------- /tests/structs/struct_test.v: -------------------------------------------------------------------------------- 1 | struct AnotherTestStruct { 2 | a_int int 3 | a_string string 4 | } 5 | 6 | fn (t AnotherTestStruct) sum(n int) int { 7 | return t.a_int + n 8 | } 9 | 10 | fn (t &AnotherTestStruct) ref_sum(n int) int { 11 | return t.a_int + n 12 | } 13 | 14 | fn (t &AnotherTestStruct) ref_sum_ref_arg(n &int) int { 15 | return t.a_int + n 16 | } 17 | 18 | fn (mut t AnotherTestStruct) add(n int) { 19 | t.a_int = t.a_int + n 20 | } 21 | 22 | fn test_struct_fn() { 23 | ts := AnotherTestStruct{ 24 | a_int: 100 25 | a_string: 'hello' 26 | } 27 | res := ts.sum(10) 28 | assert res == 110 29 | 30 | res2 := ts.ref_sum(20) 31 | assert res2 == 120 32 | 33 | res3 := ts.ref_sum(30) 34 | assert res3 == 130 35 | 36 | ts.add(100) 37 | assert ts.a_int == 200 38 | } 39 | -------------------------------------------------------------------------------- /tests/variables/arrays_test.v: -------------------------------------------------------------------------------- 1 | fn test_basic_fixed_value_array() { 2 | arr := [1,2,3,4]! 3 | 4 | for i in 1..5 { 5 | assert arr[i-1] == i 6 | } 7 | } -------------------------------------------------------------------------------- /tests/variables/mutable_test.v: -------------------------------------------------------------------------------- 1 | struct TestStruct { 2 | i int 3 | s string 4 | } 5 | 6 | fn mutable_struct_function_param(mut t TestStruct) { 7 | t.i = 200 8 | } 9 | 10 | fn no_mutable_struct_function_param(t TestStruct) { 11 | assert t.i == 100 12 | } 13 | 14 | 15 | fn mutable_function_param(mut x int) { 16 | x = 10 17 | } 18 | 19 | fn no_mut_or_ref(x int) { 20 | assert x == 10 21 | } 22 | fn no_mut_but_ref(x &int) { 23 | assert x == 10 24 | } 25 | fn mutable_function_param_with_assert(mut x int) { 26 | x = 10 27 | assert x == 10 28 | no_mut_or_ref(x) 29 | no_mut_but_ref(x) 30 | } 31 | 32 | fn test_mutable_param() { 33 | mut x:= 100 34 | mutable_function_param(mut x) 35 | assert x == 10 36 | } 37 | 38 | fn test_what_ever() { 39 | assert true 40 | } 41 | fn test_anything() { 42 | assert true 43 | } 44 | fn test_mutable_struct_param() { 45 | mut ts := TestStruct{ 46 | i: 100 47 | s: 'word' 48 | } 49 | mutable_struct_function_param(mut ts) 50 | assert ts.i == 200 51 | 52 | mut x := TestStruct{ 53 | i: 100 54 | s: 'word' 55 | } 56 | x.i = 300 57 | mutable_struct_function_param(mut x) 58 | assert x.i == 200 59 | // todo test string when compare string works 60 | } 61 | 62 | fn test_non_mutable_struct_param() { 63 | no_mutable_struct_function_param(TestStruct{ 64 | i: 100 65 | s: 'word' 66 | }) 67 | } 68 | 69 | fn test_mutable_with_operators() { 70 | mut a := 1 71 | mut b := 2 72 | assert a + b == 3 73 | } 74 | 75 | 76 | fn sum(x &int, y int, mut res int) { 77 | res = x + y 78 | } 79 | 80 | fn test_sum_with_ref() { 81 | a := 1 82 | b := 2 83 | mut res := 0 84 | sum(a, b, mut res) 85 | assert res == 3 86 | sum(b, 20, mut res) 87 | assert res == 22 88 | } -------------------------------------------------------------------------------- /tests/variables/variables_test.v: -------------------------------------------------------------------------------- 1 | fn test_basic_variable() { 2 | a := 1 3 | b := 2 4 | assert a + b == 3 5 | } -------------------------------------------------------------------------------- /v.mod: -------------------------------------------------------------------------------- 1 | Module { 2 | name: 'v', 3 | description: 'A minimal learning v compiler.', 4 | version: '0.0.1' 5 | dependencies: [] 6 | } --------------------------------------------------------------------------------