'use' statements work; only initial version
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 2 Jul 2020 02:34:10 +0000 (21:34 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Thu, 2 Jul 2020 02:34:10 +0000 (21:34 -0500)
16 files changed:
.vimspector.json
docs/thoughts
include/onyxparser.h
misc/onyx.vim
onyx
progs/break_test.onyx
progs/game.onyx [deleted file]
progs/minimal.onyx
progs/mvp.onyx [deleted file]
progs/new_minimal.onyx
progs/other.onyx
progs/print_funcs.onyx [new file with mode: 0644]
progs/test.onyx
src/onyx.c
src/onyxparser.c
src/onyxutils.c

index b101bde4314e9348fcc1ea1a90161183b631e2d9..e309f7f9bf9838e0c2649137abd51c35f11dd304 100644 (file)
@@ -6,7 +6,7 @@
                 "type": "cppdbg",
                 "request": "launch",
                 "program": "${workspaceFolder}/onyx",
-                "args": ["-ast", "progs/other.onyx", "progs/test.onyx"],
+                "args": ["progs/test.onyx"],
                 "stopAtEntry": true,
                 "cwd": "${workspaceFolder}",
                 "environment": [],
index de164d117d9df84c6cc16f17e8a230305ab10c72..639488db595bbf374229bb4b3bc036ab5c63b4c6 100644 (file)
@@ -1,26 +1,18 @@
-Type checking at parse time:\r
-    Why couldn't this work?\r
-    * Every variable is of known type or the type must be known by immediate assignment\r
-    * This requires that functions are declared in a particular order like C\r
-    * This also requires immediate evaluation of external symbols (C #include style)\r
-        - Don't like this at all\r
-        - Want a proper module system <<<<\r
+Memory design:\r
+    - Pointers will work very similar to how they do in C\r
+        - A pointer is a u32\r
+        - Pointers will be notated:\r
+            ^u32  <- Pointer to u32\r
 \r
-    /* foo.onyx */\r
-    foo :: proc (a i32) -> f32 {\r
-        return a as f32;\r
-    }\r
+        - Pointer operations will be:\r
+            * will take the address of a value\r
+                - This operation will not be defined well for a while\r
+                - You can't take the address of a local since it doesn't exist in memory\r
 \r
+            << will take the value out of a pointer\r
 \r
-    /* main.onyx */\r
-    use "foo";\r
-\r
-    export main :: proc () -> void {\r
-        a := 2.0f + foo(5);\r
-    }\r
-\r
-    foo(5) would have a left node of SYMBOL:foo\r
-    This will be resolved in a later stage between the parsing and semantic pass\r
-    Type checking and resolving would have to occur afterwards\r
-\r
-Creating an IR:\r
+            Example use:\r
+            {{{\r
+                ptr: ^i32 = 0; // Address starting at 0\r
+                ptr_ptr := ^ptr;\r
+            }}}\r
index 6f256564a81cc1c5f5ee217c8e4ab1462321f6fc..137a1987d7f3226503fe29c3c2b5f96161ececb1 100644 (file)
@@ -20,6 +20,7 @@ typedef struct OnyxAstNodeFuncDef OnyxAstNodeFuncDef;
 typedef struct OnyxAstNodeForeign OnyxAstNodeForeign;
 typedef struct OnyxAstNodeGlobal OnyxAstNodeGlobal;
 typedef struct OnyxAstNodeCall OnyxAstNodeCall;
+typedef struct OnyxAstNodeUse OnyxAstNodeUse;
 typedef struct OnyxAstNodeFile OnyxAstNodeFile;
 
 typedef struct OnyxParser {
@@ -38,6 +39,7 @@ typedef struct OnyxParser {
 typedef enum OnyxAstNodeKind {
     ONYX_AST_NODE_KIND_ERROR,
     ONYX_AST_NODE_KIND_PROGRAM,
+    ONYX_AST_NODE_KIND_USE,
 
     ONYX_AST_NODE_KIND_FUNCDEF,
     ONYX_AST_NODE_KIND_FOREIGN,
@@ -265,6 +267,16 @@ struct OnyxAstNodeCall {
                                 // unless this becomes used by something else
 };
 
+struct OnyxAstNodeUse {
+    OnyxAstNodeKind kind;
+    u32 flags;
+    OnyxToken *token;
+    OnyxTypeInfo *type;
+    u64 data;
+    OnyxAstNode *next;
+    OnyxToken *filename;
+};
+
 struct OnyxAstNodeFile {
     OnyxAstNodeKind kind;
     u32 flags;
@@ -302,6 +314,7 @@ union OnyxAstNode {
     OnyxAstNodeGlobal as_global;
     OnyxAstNodeIf as_if;
     OnyxAstNodeWhile as_while;
+    OnyxAstNodeUse as_use;
     OnyxAstNodeFile as_file;
 };
 
index 6a37596621c11114515ba854e885d44c9bca6c47..8edffc144de601d0b7be8574d9b9b328df71c30c 100644 (file)
@@ -16,8 +16,9 @@ syn keyword onyxKeyword for while loop return do
 syn keyword onyxKeyword break continue return
 syn keyword onyxKeyword as
 
-syn keyword onyxType i32
-syn keyword onyxType i64
+syn keyword onyxType unknown bool
+syn keyword onyxType i32 u32
+syn keyword onyxType i64 u64
 syn keyword onyxType f32
 syn keyword onyxType f64
 
diff --git a/onyx b/onyx
index eb5f54188bf77f3602b68b7d3035a7919f85bbc0..c8eaac115b5090e2c5c48a211ceb77d0c451fd4f 100755 (executable)
Binary files a/onyx and b/onyx differ
index c5cb7b20ee80579a2a4363b75a070ea8a780d6c5..f2847cca11bede00e0fd6daa5bc21d2b232b7580 100644 (file)
@@ -1,5 +1,5 @@
 
-print :: foreign "host" "print" proc (val i32) ---
+print :: foreign "host" "print" proc (val: i32) ---
 
 export main :: proc {
 
diff --git a/progs/game.onyx b/progs/game.onyx
deleted file mode 100644 (file)
index a37d4c5..0000000
+++ /dev/null
@@ -1,14 +0,0 @@
-
-gfx_draw_rect :: foreign "gfx" "draw_rect" proc (x i32, y i32, w i32, h i32) ---
-
-export main :: proc {
-    update();
-    draw();
-}
-
-update :: proc {}
-
-draw :: proc {
-    gfx_draw_rect(0, 0, 100, 100);
-}
-
index 7fd39bb618fd9facbb2e8382bdc1a4bf68159689..3d4fac4ff8754d99d7c4235cc8ff0f63f243d4bb 100644 (file)
@@ -1,7 +1,7 @@
 
-print       :: foreign "host" "print" proc (value i32) ---
-print_float :: foreign "host" "print" proc (value f32) ---
-print_if    :: foreign "host" "print" proc (i i32, f f32) ---
+print       :: foreign "host" "print" proc (value: i32) ---
+print_float :: foreign "host" "print" proc (value: f32) ---
+print_if    :: foreign "host" "print" proc (i: i32, f: f32) ---
 
 export main :: proc {
     output := do_stuff() - 1;
@@ -25,7 +25,7 @@ export main :: proc {
     print_if(output, float_test());
 }
 
-factorial :: proc (n i32) -> i32 {
+factorial :: proc (n: i32) -> i32 {
     if n <= 1 { return 1; }
 
     return n * factorial(n - 1);
@@ -35,7 +35,7 @@ foo :: proc -> i32 {
     return 10;
 }
 
-add :: proc (a i32, b i32) -> i32 {
+add :: proc (a: i32, b: i32) -> i32 {
     return a + b;
 }
 
@@ -43,18 +43,18 @@ add :: proc (a i32, b i32) -> i32 {
 // This is because the WASM embedder does not think that it
 // is possible that all code paths are covered with returning
 // an i32. This will need to be fixed.
-abs :: proc (val i32) -> i32 {
+abs :: proc (val: i32) -> i32 {
     if val <= 0 { return -val; }
     // else { return val; };
     return val;
 }
 
-fib :: proc (n i32) -> i32 {
+fib :: proc (n: i32) -> i32 {
     if n <= 1 { return 1; }
     return fib(n - 1) + fib(n - 2);
 }
 
-diff_square :: proc (a i32, b i32) -> i32 {
+diff_square :: proc (a: i32, b: i32) -> i32 {
     // Typechecked
     c := a - b; // Mutable
     d :: a + b; // Constant
diff --git a/progs/mvp.onyx b/progs/mvp.onyx
deleted file mode 100644 (file)
index f75b65d..0000000
+++ /dev/null
@@ -1,21 +0,0 @@
-foreign console {
-    log :: proc (data ptr, length i32) -> void ---;
-}
-
-export add :: proc (a i32, b i32) -> i32 {
-    return a + b;
-}
-
-export max :: proc (a i32, b i32) -> i32 {
-    // Curly braces are always required
-    if a > b {
-        return a;
-    } else {
-        return b;
-    }
-}
-
-export main :: proc () {
-    console.log(add(2, 3));
-    console.log(max(5, 10));
-}
index d0e7432387b3e9f14b640ed4cea006ac4518675c..d561a466998816045a9add2acd17f74a5653b8d0 100644 (file)
@@ -1,4 +1,4 @@
-print :: foreign "host" "print" proc (value i32) ---
+print :: foreign "host" "print" proc (value: i32) ---
 
 simple_test :: proc {
     a: i32 = 5;
@@ -13,7 +13,7 @@ simple_test :: proc {
     print(c);
 }
 
-fib :: proc (n i32) -> i32 {
+fib :: proc (n: i32) -> i32 {
     if n <= 1 { return 1; }
 
     return fib(n - 1) + fib(n - 2);
@@ -23,7 +23,7 @@ foo :: proc -> i32 {
     return 10;
 }
 
-print_nums :: proc (a i32, b i32, c i32) {
+print_nums :: proc (a: i32, b: i32, c: i32) {
     print(a);
     print(b);
     print(c);
index 253c1a489c681017d7caf190005ab4ff1555b56e..a9202ace930630c623ac2313a76e3bd028023293 100644 (file)
@@ -1,9 +1,11 @@
 
-other_value :: proc (n i32) -> i32 {
+use "progs/test"
+
+other_value :: proc (n: i32) -> i32 {
     return 8675309 + something_else(n) + global_value;
 }
 
-export fib :: proc (n i32) -> i32 {
+export fib :: proc (n: i32) -> i32 {
     if n == 0 { return 1; }
     if n == 1 { return 1; }
 
@@ -23,7 +25,7 @@ export fib :: proc (n i32) -> i32 {
     return a;
 }
 
-export factorial :: proc (n i32) -> i32 {
+export factorial :: proc (n: i32) -> i32 {
     if n <= 1 { return 1; }
 
     f := 1;
diff --git a/progs/print_funcs.onyx b/progs/print_funcs.onyx
new file mode 100644 (file)
index 0000000..bb2a3d6
--- /dev/null
@@ -0,0 +1,5 @@
+print_bool :: foreign "host" "print" proc (value: bool) ---
+print_i32  :: foreign "host" "print" proc (value: i32) ---
+print_f32  :: foreign "host" "print" proc (value: f32) ---
+print_i64  :: foreign "host" "print" proc (value: i64) ---
+print_f64  :: foreign "host" "print" proc (value: f64) ---
index 540694305104e24e123180d47ba711317dabc5e2..746003845519d3701124189d9a797771afa481a6 100644 (file)
@@ -1,27 +1,15 @@
+use "progs/print_funcs"
+use "progs/other"
 
-// Foreign functions are included this way:
-//      sym_name :: foreign "module" "name" proc ...
-
-// TODO: Make this work
-// use "progs/other";
-
-print_i32 :: foreign "host" "print" proc (value i32) ---
-print_f32 :: foreign "host" "print" proc (value f32) ---
-print_i64 :: foreign "host" "print" proc (value i64) ---
-print_f64 :: foreign "host" "print" proc (value f64) ---
-
-something_else :: proc (n i32) -> i32 {
+something_else :: proc (n: i32) -> i32 {
     return 100 * n + global_value;
 }
 
-// symbol :: proc {}            Global function
-// symbol :: struct { x i32, y i32 }
-// symbol :: foreign "" "" proc Global foreign function
-// symbol :: foreign "" "" i32  Global foreign mutable i32
-// symbol :: 5                  Global constant value
-// symbol := 5                  Global mutable variable
+global_value := 100
 
-export global_value := 100
+export in_unit_circle :: proc (x: f32, y: f32) -> bool {
+    return (x * x) + (y * y) < 1.0f;
+}
 
 // This is the entry point
 export main2 :: proc {
index 4d3fafca35ba8179becc88fd312b56a09964ddde..c7decffffaf08ad36dbb9e6b7a4176bcfcd275ae 100644 (file)
 
 static const char* docstring = "Onyx compiler version " VERSION "\n"
     "\n"
-    "The standard compiler for the Onyx programming language.\n"
+    "The compiler for the Onyx programming language.\n"
     "\n"
-    " $ onyx [-o <target file>] [--help] <input files>\n"
-    "\n"
-    "   -o <target_file>        Specify the target file\n"
-    "   --help                  Print this help message\n";
+    "Usage:\n"
+    "\tonyx [-o <target file>] [-ast] <input files>\n"
+    "\tonyx -help\n"
+    "\nFlags:\n"
+    "\t-o <target_file>        Specify the target file (default: out.wasm)\n"
+    "\t-ast                    Print the abstract syntax tree after parsing\n"
+    "\t-help                   Print this help message\n";
 
 typedef enum CompileAction {
     ONYX_COMPILE_ACTION_COMPILE,
@@ -48,7 +51,12 @@ typedef enum CompilerProgress {
 typedef struct CompilerState {
     bh_arena ast_arena, msg_arena, sp_arena;
     bh_allocator token_alloc, ast_alloc, msg_alloc, sp_alloc;
+
     bh_table(bh_file_contents) loaded_files;
+    bh_arr(const char *) queued_files;
+
+    OnyxAstNodeFile* first_file;
+    OnyxAstNodeFile* last_processed_file;
 
     OnyxMessages msgs;
     OnyxWasmModule wasm_mod;
@@ -68,7 +76,7 @@ static OnyxCompileOptions compile_opts_parse(bh_allocator alloc, int argc, char
     bh_arr_new(alloc, options.files, 1);
 
     fori(i, 1, argc - 1) {
-        if (!strcmp(argv[i], "--help")) {
+        if (!strcmp(argv[i], "-help")) {
             options.action = ONYX_COMPILE_ACTION_PRINT_HELP;
             break;
         }
@@ -92,17 +100,78 @@ void compile_opts_free(OnyxCompileOptions* opts) {
     bh_arr_free(opts->files);
 }
 
-OnyxAstNodeFile* parse_source_file(bh_file_contents* file_contents, CompilerState* compiler_state) {
+OnyxAstNodeFile* parse_source_file(CompilerState* compiler_state, bh_file_contents* file_contents) {
     // NOTE: Maybe don't want to recreate the tokenizer and parser for every file
     OnyxTokenizer tokenizer = onyx_tokenizer_create(compiler_state->token_alloc, file_contents);
-    bh_printf("Lexing  %s\n", file_contents->filename);
+    bh_printf("[Lexing]       %s\n", file_contents->filename);
     onyx_lex_tokens(&tokenizer);
 
-    bh_printf("Parsing %s\n", file_contents->filename);
+    bh_printf("[Parsing]      %s\n", file_contents->filename);
     OnyxParser parser = onyx_parser_create(compiler_state->ast_alloc, &tokenizer, &compiler_state->msgs);
     return onyx_parse(&parser);
 }
 
+CompilerProgress process_source_file(CompilerState* compiler_state, OnyxCompileOptions* opts, char* filename) {
+    if (bh_table_has(bh_file_contents, compiler_state->loaded_files, filename)) return ONYX_COMPILER_PROGRESS_SUCCESS;
+
+    bh_file file;
+
+    bh_file_error err = bh_file_open(&file, filename);
+    if (err != BH_FILE_ERROR_NONE) {
+        bh_printf_err("Failed to open file %s\n", filename);
+        return ONYX_COMPILER_PROGRESS_FAILED_READ;
+    }
+
+    bh_printf("[Reading]      %s\n", file.filename);
+    bh_file_contents fc = bh_file_read_contents(compiler_state->token_alloc, &file);
+    bh_file_close(&file);
+
+    bh_table_put(bh_file_contents, compiler_state->loaded_files, (char *) filename, fc);
+    // NOTE: Need to reget the value out of the table so token references work
+    fc = bh_table_get(bh_file_contents, compiler_state->loaded_files, (char *) filename);
+
+    OnyxAstNodeFile* file_node = parse_source_file(compiler_state, &fc);
+
+    if (opts->print_ast) {
+        onyx_ast_print((OnyxAstNode *) file_node, 0);
+        bh_printf("\n");
+    }
+
+    if (!compiler_state->first_file)
+        compiler_state->first_file = file_node;
+
+    if (compiler_state->last_processed_file)
+        compiler_state->last_processed_file->next = file_node;
+
+    compiler_state->last_processed_file = file_node;
+
+
+    // HACK: This is very cobbled together right now but it will
+    // do for now
+    OnyxAstNode* walker = file_node->contents;
+    while (walker) {
+        if (walker->kind == ONYX_AST_NODE_KIND_USE) {
+            OnyxAstNodeUse* use_node = &walker->as_use;
+
+            char* formatted_name = bh_aprintf(
+                    global_heap_allocator,
+                    "%b.onyx",
+                    use_node->filename->token, use_node->filename->length);
+
+            bh_arr_push(compiler_state->queued_files, formatted_name);
+        }
+
+        walker = walker->next;
+    }
+
+
+    if (onyx_message_has_errors(&compiler_state->msgs)) {
+        return ONYX_COMPILER_PROGRESS_FAILED_PARSE;
+    } else {
+        return ONYX_COMPILER_PROGRESS_SUCCESS;
+    }
+}
+
 i32 onyx_compile(OnyxCompileOptions* opts, CompilerState* compiler_state) {
 
     bh_arena_init(&compiler_state->msg_arena, opts->allocator, 4096);
@@ -120,71 +189,53 @@ i32 onyx_compile(OnyxCompileOptions* opts, CompilerState* compiler_state) {
     bh_arena_init(&compiler_state->sp_arena, opts->allocator, 16 * 1024);
     compiler_state->sp_alloc = bh_arena_allocator(&compiler_state->sp_arena);
 
-    bh_table_init(opts->allocator, compiler_state->loaded_files, 7);
+    bh_table_init(opts->allocator, compiler_state->loaded_files, 15);
 
-    bh_arr_each(const char *, filename, opts->files) {
-        bh_file file;
+    bh_arr_new(opts->allocator, compiler_state->queued_files, 4);
 
-        bh_file_error err = bh_file_open(&file, *filename);
-        if (err != BH_FILE_ERROR_NONE) {
-            bh_printf_err("Failed to open file %s\n", *filename);
-            return ONYX_COMPILER_PROGRESS_FAILED_READ;
-        }
+    // NOTE: Add all files passed by command line to the queue
+    bh_arr_each(const char *, filename, opts->files)
+        bh_arr_push(compiler_state->queued_files, (char *) *filename);
 
-        bh_printf("Reading %s\n", file.filename);
-        bh_file_contents fc = bh_file_read_contents(compiler_state->token_alloc, &file);
-        bh_file_close(&file);
 
-        bh_table_put(bh_file_contents, compiler_state->loaded_files, (char *) filename, fc);
-    }
+    // NOTE: While the queue is not empty, process the next file
+    while (!bh_arr_is_empty(compiler_state->queued_files)) {
+        CompilerProgress result = process_source_file(compiler_state, opts, (char *) compiler_state->queued_files[0]);
 
-    OnyxAstNodeFile* root_file = NULL;
-    OnyxAstNodeFile* prev_file = NULL;
-    bh_table_each_start(bh_file_contents, compiler_state->loaded_files);
-        OnyxAstNodeFile* file_node = parse_source_file(&value, compiler_state);
-
-        if (opts->print_ast) {
-            onyx_ast_print((OnyxAstNode *) file_node, 0);
-            bh_printf("\n");
-        }
+        if (result != ONYX_COMPILER_PROGRESS_SUCCESS)
+            return result;
 
-        if (!root_file) {
-            root_file = file_node;
-        }
-
-        if (prev_file) {
-            prev_file->next = file_node;
-        }
-
-        prev_file = file_node;
-    bh_table_each_end;
-
-    if (onyx_message_has_errors(&compiler_state->msgs)) {
-        return ONYX_COMPILER_PROGRESS_FAILED_PARSE;
+        bh_arr_fastdelete(compiler_state->queued_files, 0);
     }
 
-    bh_printf("Checking semantics and types\n");
+
+    // NOTE: Check types and semantic rules
+    bh_printf("[Checking semantics]\n");
     OnyxSemPassState sp_state = onyx_sempass_create(compiler_state->sp_alloc, compiler_state->ast_alloc, &compiler_state->msgs);
-    onyx_sempass(&sp_state, root_file);
+    onyx_sempass(&sp_state, compiler_state->first_file);
 
     if (onyx_message_has_errors(&compiler_state->msgs)) {
         return ONYX_COMPILER_PROGRESS_FAILED_SEMPASS;
     }
 
-    bh_printf("Creating WASM code\n");
+
+    // NOTE: Generate WASM instructions
+    bh_printf("[Generating WASM]\n");
     compiler_state->wasm_mod = onyx_wasm_module_create(opts->allocator, &compiler_state->msgs);
-    onyx_wasm_module_compile(&compiler_state->wasm_mod, root_file);
+    onyx_wasm_module_compile(&compiler_state->wasm_mod, compiler_state->first_file);
 
     if (onyx_message_has_errors(&compiler_state->msgs)) {
         return ONYX_COMPILER_PROGRESS_FAILED_BINARY_GEN;
     }
 
+
+    // NOTE: Output to file
     bh_file output_file;
     if (bh_file_create(&output_file, opts->target_file) != BH_FILE_ERROR_NONE) {
         return ONYX_COMPILER_PROGRESS_FAILED_OUTPUT;
     }
 
-    bh_printf("Writing WASM to %s\n", output_file.filename);
+    bh_printf("[Writing WASM] %s\n", output_file.filename);
     onyx_wasm_module_write_to_file(&compiler_state->wasm_mod, output_file);
 
     return ONYX_COMPILER_PROGRESS_SUCCESS;
index 3a698b6abc4fcc7023cd72577868f32caf9007e4..5cf6f232b0118d5828f85fdd488bee0544dd2dac 100644 (file)
@@ -6,6 +6,7 @@
 static const char* ast_node_names[] = {
     "ERROR",
     "PROGRAM",
+    "USE",
 
     "FUNCDEF",
     "FOREIGN",
@@ -669,6 +670,7 @@ static OnyxAstNodeParam* parse_function_params(OnyxParser* parser) {
         if (parser->curr_token->type == TOKEN_TYPE_SYM_COMMA) parser_next_token(parser);
 
         symbol = expect(parser, TOKEN_TYPE_SYMBOL);
+        expect(parser, TOKEN_TYPE_SYM_COLON);
 
         curr_param = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_PARAM);
         curr_param->token = symbol;
@@ -759,8 +761,13 @@ static OnyxAstNode* parse_top_level_constant_symbol(OnyxParser* parser) {
 static OnyxAstNode* parse_top_level_statement(OnyxParser* parser) {
     switch (parser->curr_token->type) {
         case TOKEN_TYPE_KEYWORD_USE:
-            assert(0);
-            break;
+            {
+                OnyxAstNodeUse* use_node = onyx_ast_node_new(parser->allocator, ONYX_AST_NODE_KIND_USE);
+                use_node->token = expect(parser, TOKEN_TYPE_KEYWORD_USE);
+                use_node->filename = expect(parser, TOKEN_TYPE_LITERAL_STRING);
+
+                return (OnyxAstNode *) use_node;
+            }
 
         case TOKEN_TYPE_KEYWORD_EXPORT:
             {
index cae6546a407e659b297d5d6f0a2eccff44591e90..b40a44c0f2832684ec6dad2b4b53f1bf4c2060e2 100644 (file)
@@ -28,6 +28,16 @@ void onyx_ast_print(OnyxAstNode* node, i32 indent) {
         break;
     }
 
+    case ONYX_AST_NODE_KIND_USE: {
+         OnyxAstNodeUse* use_node = &node->as_use;
+         bh_printf("%b", use_node->filename->token, use_node->filename->length);
+
+         if (use_node->next)
+             onyx_ast_print(use_node->next, indent);
+
+         break;
+     }
+
     case ONYX_AST_NODE_KIND_FUNCDEF: {
         if (node->token)
             bh_printf("(%b) ", node->token->token, node->token->length);