nitron

commit 9708e3eba052b84365f8461b1920169ef21c2269

Author: mesyeti <mesyeti@mesyeti.uk>

add imports

 basic/lib.bas | 5 ++
 basic/source/compiler.c | 26 ++++++++---
 basic/source/compiler.h | 2 
 basic/source/frontend/c89.c | 54 +++++++++++++++++++++++--
 basic/source/frontend/c89.h | 2 
 basic/source/import.c | 59 ++++++++++++++++++++++++++++
 basic/source/import.h | 13 ++++++
 basic/source/lexer.c | 4 +
 basic/source/lexer.h | 2 
 basic/source/main.c | 44 +++++++++++++++++++-
 basic/source/options.c | 6 ++
 basic/source/options.h | 14 ++++++
 basic/source/parser.c | 73 ++++++++++++++++++++++++++++++++--
 basic/source/parser.h | 10 ++++
 basic/source/semanticAnalysis.c | 2 
 basic/source/state.c | 25 +++++++++++
 basic/source/state.h | 1 
 basic/source/util.c | 26 ++++++++++++
 basic/source/util.h | 4 +
 basic/test.bas | 14 +----


diff --git a/basic/lib.bas b/basic/lib.bas
new file mode 100644
index 0000000000000000000000000000000000000000..f526bfdebfd7c3243eb9b8249b13d5491622decc
--- /dev/null
+++ b/basic/lib.bas
@@ -0,0 +1,5 @@
+extern func puts(str as ptr(char)) i32
+
+sub PrintStrLn(str as ptr(char))
+	puts(str)
+end




diff --git a/basic/source/compiler.c b/basic/source/compiler.c
index d990335cd0f2ef79f3a16773d0b98f4c3add6ac2..be44878fc65b5df8a663184962e55f7e58d23efc 100644
--- a/basic/source/compiler.c
+++ b/basic/source/compiler.c
@@ -41,9 +41,11 @@ 			case NODE_DIM:        break;
 			case NODE_EXTERN:     break;
 			case NODE_TYPE_DEF:   break;
 			case NODE_TYPE_BLOCK: break;
-			case NODE_FUNC_CALL: frontend.compileFuncCall(&node->funcCall); break;
-			case NODE_IF:        frontend.compileIf(&node->ifStat);         break;
-			case NODE_WHILE:     frontend.compileWhile(&node->whileStat);   break;
+			case NODE_PROGRAM:    break;
+			case NODE_IMPORT:     break;
+			case NODE_FUNC_CALL:  frontend.compileFuncCall(&node->funcCall); break;
+			case NODE_IF:         frontend.compileIf(&node->ifStat);         break;
+			case NODE_WHILE:      frontend.compileWhile(&node->whileStat);   break;
 			case NODE_BINARY_OP: {
 				if (node->bin.op == TOKEN_ASSIGN) {
 					frontend.compileAssign(&node->bin);
@@ -66,18 +68,26 @@ 	}
 }
 
 void Compiler_Run(void) {
+	bool program = false;
+
 	for (size_t i = 0; i < compiler.len; ++ i) {
 		Node* node = &compiler.nodes[i];
 		Compiler_CompileTopNode(node, false);
+
+		if (node->i.type == NODE_PROGRAM) {
+			program = true;
+		}
 	}
 
-	frontend.beginMain();
+	if (program) {
+		frontend.beginMain();
 
-	for (size_t i = 0; i < compiler.len; ++ i) {
-		Node* node = &compiler.nodes[i];
+		for (size_t i = 0; i < compiler.len; ++ i) {
+			Node* node = &compiler.nodes[i];
 
-		Compiler_CompileTopNode(node, true);
+			Compiler_CompileTopNode(node, true);
+		}
 	}
 
-	frontend.finish();
+	frontend.finish(program);
 }




diff --git a/basic/source/compiler.h b/basic/source/compiler.h
index a6dea3c1f4865a22ba92efdef4d07d16faa865e0..7cbc036cf50b6ad3ce9329f9063de654d0cdc20f 100644
--- a/basic/source/compiler.h
+++ b/basic/source/compiler.h
@@ -4,7 +4,7 @@
 #include "parser.h"
 
 typedef struct {
-	void (*finish)(void);
+	void (*finish)(bool program);
 	void (*beginMain)(void);
 	void (*compileFuncDef)(FuncDefNode* node);
 	void (*compileDim)(DimNode* node);




diff --git a/basic/source/frontend/c89.c b/basic/source/frontend/c89.c
index 5294a3098d097ffd76a25ef0c420532b3f766c32..c9c894aecf0889005bd5c8f36e5b5c7dd51c67ec 100644
--- a/basic/source/frontend/c89.c
+++ b/basic/source/frontend/c89.c
@@ -1,17 +1,27 @@
 #include <stdio.h>
 #include <assert.h>
 #include "c89.h"
+#include "../util.h"
 #include "../state.h"
+#include "../options.h"
 #include "../semanticAnalysis.h"
 
 static FILE* out;
 static bool  inMain;
 
-static void Finish(void) {
-	fprintf(out, "}\n");
+static void Finish(bool program) {
+	if (program) {
+		fprintf(out, "}\n");
+	}
 	fflush(out);
 
-	const char* cmd = "gcc out.c -o out -fno-builtin";
+	char cmd[512];
+
+	snprintf(
+		cmd, 512, "gcc %s %s.c -o %s -fno-builtin",
+		options.obj? "-c" : "", options.out, options.out
+	);
+
 	puts(cmd);
 	system(cmd);
 }
@@ -249,9 +259,18 @@
 	fprintf(out, "} nitron_%s;", type->name);
 }
 
-Frontend Frontend_C89(void) {
-	out    = fopen("out.c", "w");
+Frontend Frontend_C89() {
+	char* path = ConcatString(options.out, ".c");
+
+	out    = fopen(path, "w");
 	inMain = false;
+
+	if (!out) {
+		fprintf(stderr, "Failed to open '%s'\n", path);
+		exit(1);
+	}
+
+	free(path);
 
 	fprintf(out, "#include <stdint.h>\n");
 	fprintf(out, "#include <stdlib.h>\n");
@@ -275,6 +294,31 @@ 		Var* var = &state.scopes[0][i];
 
 		CompileUsedType(var->type);
 		fprintf(out, " nitron_%s;\n", var->name);
+	}
+
+	// declare all functions
+	for (size_t i = 0; i < state.funcNum; ++ i) {
+		Function* func = &state.funcs[i];
+
+		if (func->sub) {
+			fprintf(out, "void");
+		}
+		else {
+			CompileUsedType(func->ret);
+		}
+
+		fprintf(out, " %s(", func->name);
+
+		for (size_t j = 0; j < func->paramsLen; ++ j) {
+			CompileUsedType(func->params[j].type);
+			fprintf(out, " %s", func->params[j].name);
+
+			if (j < func->paramsLen - 1) {
+				fprintf(out, ", ");
+			}
+		}
+
+		fprintf(out, ");\n");
 	}
 
 	return (Frontend) {




diff --git a/basic/source/frontend/c89.h b/basic/source/frontend/c89.h
index 6edada43c8f3f07c258e762f901b999433737a98..081b217add16ac7b107ab5b813affee1e7ec66eb 100644
--- a/basic/source/frontend/c89.h
+++ b/basic/source/frontend/c89.h
@@ -3,6 +3,6 @@ #define N_FRONTEND_C89_H
 
 #include "../compiler.h"
 
-Frontend Frontend_C89(void);
+Frontend Frontend_C89();
 
 #endif




diff --git a/basic/source/import.c b/basic/source/import.c
new file mode 100644
index 0000000000000000000000000000000000000000..27f5a8bd2e8be5dc46d9442b968de533aa28a604
--- /dev/null
+++ b/basic/source/import.c
@@ -0,0 +1,59 @@
+#include "mem.h"
+#include "util.h"
+#include "state.h"
+#include "import.h"
+
+static char** paths;
+static size_t pathsLen;
+
+void Import_Init(void) {
+	paths    = NULL;
+	pathsLen = 0;
+}
+
+void Import_Free(void) {
+	if (paths) {
+		free(paths);
+	}
+}
+
+void Import_AddPath(const char* path) {
+	++ pathsLen;
+
+	paths               = SafeRealloc(paths, pathsLen * sizeof(char*));
+	paths[pathsLen - 1] = NewString(path);
+}
+
+const char* Import_Search(const char* name) {
+	for (size_t i = 0; i < pathsLen; ++ i) {
+		// TODO: improve using some string builder thingy
+		char* str1 = ConcatString(paths[i], "/");
+		char* str2 = ConcatString(str1, name);
+		char* str3 = ConcatString(str2, ".bas");
+		free(str1);
+		free(str2);
+
+		if (FileExists(str3)) {
+			return str3;
+		}
+
+		free(str3);
+	}
+
+	return NULL;
+}
+
+void Import_Run(Node* nodes, size_t len) {
+	for (size_t i = 0; i < len; ++ i) {
+		if (nodes[i].i.type == NODE_IMPORT) {
+			const char* path = Import_Search(nodes[i].import.name);
+
+			if (!path) {
+				PrintError(nodes[i].i.err, "Couldn't find '%s'", nodes[i].import.name);
+			}
+			if (!State_ImportFile(path)) {
+				PrintError(nodes[i].i.err, "Failed to import '%s'", nodes[i].import.name);
+			}
+		}
+	}
+}




diff --git a/basic/source/import.h b/basic/source/import.h
new file mode 100644
index 0000000000000000000000000000000000000000..28b8f2ec4160bb17d64470699ab60b1b8f09f2e1
--- /dev/null
+++ b/basic/source/import.h
@@ -0,0 +1,13 @@
+#ifndef N_IMPORT_H
+#define N_IMPORT_H
+
+#include "common.h"
+#include "parser.h"
+
+void        Import_Init(void);
+void        Import_Free(void);
+void        Import_AddPath(const char* path);
+const char* Import_Search(const char* name);
+void        Import_Run(Node* nodes, size_t len);
+
+#endif




diff --git a/basic/source/lexer.c b/basic/source/lexer.c
index f89957c27c34fce0e6732624e39f333853c991d0..90404adda62e05457c7b27eae04ed267139b0c24 100644
--- a/basic/source/lexer.c
+++ b/basic/source/lexer.c
@@ -178,7 +178,9 @@ 				{"else",     TOKEN_ELSE},
 				{"while",    TOKEN_WHILE},
 				{"do",       TOKEN_DO},
 				{"type",     TOKEN_TYPE},
-				{"union",    TOKEN_UNION}
+				{"union",    TOKEN_UNION},
+				{"program",  TOKEN_PROGRAM},
+				{"import",   TOKEN_IMPORT}
 			};
 
 			for (size_t i = 0; i < sizeof(keywords) / sizeof(Keyword); ++ i) {




diff --git a/basic/source/lexer.h b/basic/source/lexer.h
index ae41e856addb88076f84857f63faf1732a4e6918..48e11c8255f290178d8bf33d7a55184ce107b7bc 100644
--- a/basic/source/lexer.h
+++ b/basic/source/lexer.h
@@ -24,6 +24,8 @@ 	TOKEN_WHILE,
 	TOKEN_DO,
 	TOKEN_TYPE,
 	TOKEN_UNION,
+	TOKEN_PROGRAM,
+	TOKEN_IMPORT,
 
 	// misc
 	TOKEN_IDENTIFIER,




diff --git a/basic/source/main.c b/basic/source/main.c
index 9df35583c37a392556ade14513c63b1bd522fac0..363f4cdb1b3c62ff85bae89c423d439909fceaf3 100644
--- a/basic/source/main.c
+++ b/basic/source/main.c
@@ -1,12 +1,22 @@
 #include <stdio.h>
 #include <string.h>
+#include "util.h"
 #include "lexer.h"
 #include "state.h"
 #include "parser.h"
 #include "common.h"
+#include "import.h"
+#include "options.h"
 #include "semanticAnalysis.h"
 #include "frontend/c89.h"
 
+static const char* usage =
+	"Usage: %s SOURCE [FLAGS]\n\n"
+	"Flags:\n"
+	"    -i PATH - Add an import path\n"
+	"    -o FILE - Set output file\n"
+	"    --obj   - Compile to an object file instead of an executable\n";
+
 int main(int argc, char** argv) {
 	const char* source = NULL;
 
@@ -14,10 +24,11 @@ 	bool printNodes  = false;
 	bool printTokens = false;
 	bool printState  = false;
 
+	Import_Init();
+
 	if (argc == 1) {
-		printf(
-			"Usage: %s SOURCE [FLAGS]\n", argv[0]
-		);
+		printf(usage, argv[0]);
+		return 0;
 	}
 
 	for (int i = 1; i < argc; ++ i) {
@@ -38,6 +49,26 @@ 		}
 		else if (strcmp(argv[i], "--ds") == 0) {
 			printState = true;
 		}
+		else if (strcmp(argv[i], "-i") == 0) {
+			++ i;
+			if (i >= argc) {
+				fprintf(stderr, "-i expects PATH parameter\n");
+				return 1;
+			}
+
+			Import_AddPath(argv[i]);
+		}
+		else if (strcmp(argv[i], "-o") == 0) {
+			++ i;
+			if (i >= argc) {
+				fprintf(stderr, "-o expects FILE parameter\n");
+			}
+
+			options.out = argv[i];
+		}
+		else if (strcmp(argv[i], "--obj") == 0) {
+			options.obj = true;
+		}
 		else {
 			fprintf(stderr, "Unknown flag: %s\n", argv[i]);
 			return 1;
@@ -69,7 +100,12 @@
 		return 0;
 	}
 
+	char* sourceDir = DirName(source);
+	Import_AddPath(sourceDir);
+	free(sourceDir);
+
 	SemanticAnalysis_Init(8);
+	Import_Run(ret, len);
 
 	for (size_t i = 0; i < len; ++ i){
 		SemanticAnalysis_Analyse(&ret[i]);
@@ -83,6 +119,8 @@
 	Compiler_Init(ret, len);
 	Frontend_Init(Frontend_C89());
 	Compiler_Run();
+
+	Import_Free();
 
 	return 0;
 }




diff --git a/basic/source/options.c b/basic/source/options.c
new file mode 100644
index 0000000000000000000000000000000000000000..41b61f2a7cdb7e0e27d29091e69b0c828fc463dd
--- /dev/null
+++ b/basic/source/options.c
@@ -0,0 +1,6 @@
+#include "options.h"
+
+Options options = {
+	.out = "out",
+	.obj = false
+};




diff --git a/basic/source/options.h b/basic/source/options.h
new file mode 100644
index 0000000000000000000000000000000000000000..0bd01cf9b9891bb8fe629525d3ba8bd4199b165a
--- /dev/null
+++ b/basic/source/options.h
@@ -0,0 +1,14 @@
+#ifndef N_OPTIONS_H
+#define N_OPTIONS_H
+
+#include "common.h"
+
+typedef struct {
+	const char* out;
+
+	bool obj;
+} Options;
+
+extern Options options;
+
+#endif




diff --git a/basic/source/parser.c b/basic/source/parser.c
index 7d8deeac160ff733981d06c78d17548c4ef735db..5e2a744114afdf6c5f07f22353260cca243f6632 100644
--- a/basic/source/parser.c
+++ b/basic/source/parser.c
@@ -638,15 +638,44 @@ 	node.typeBlock = ret;
 	return node;
 }
 
+static Node ParseProgram(Parser* p) {
+	Advance(p);
+	Expect(p, TOKEN_IDENTIFIER);
+	Advance(p);
+	Expect(p, TOKEN_LINE);
+
+	Node node;
+	node.i = INFO(NODE_PROGRAM);
+	return node;
+}
+
+static Node ParseImport(Parser* p) {
+	ImportNode ret;
+	ret.i = INFO(NODE_IMPORT);
+
+	Advance(p);
+	Expect(p, TOKEN_IDENTIFIER);
+	ret.name = NewString(p->tokens[p->i].contents);
+
+	Advance(p);
+	Expect(p, TOKEN_LINE);
+
+	Node node;
+	node.import = ret;
+	return node;
+}
+
 static Node ParseNode(Parser* p) {
 	switch (p->tokens[p->i].type) {
 		case TOKEN_FUNC:
-		case TOKEN_SUB:    return ParseFuncDef(p);
-		case TOKEN_DIM:    return ParseDim(p);
-		case TOKEN_EXTERN: return ParseExtern(p);
-		case TOKEN_IF:     return ParseIf(p);
-		case TOKEN_WHILE:  return ParseWhile(p);
-		case TOKEN_TYPE:   return ParseTypeDef(p);
+		case TOKEN_SUB:     return ParseFuncDef(p);
+		case TOKEN_DIM:     return ParseDim(p);
+		case TOKEN_EXTERN:  return ParseExtern(p);
+		case TOKEN_IF:      return ParseIf(p);
+		case TOKEN_WHILE:   return ParseWhile(p);
+		case TOKEN_TYPE:    return ParseTypeDef(p);
+		case TOKEN_PROGRAM: return ParseProgram(p);
+		case TOKEN_IMPORT:  return ParseImport(p);
 		case TOKEN_LPAREN:
 		case TOKEN_ACCESS:
 		case TOKEN_IDENTIFIER: {
@@ -833,6 +862,14 @@ 			}
 			puts("end");
 			break;
 		}
+		case NODE_PROGRAM: {
+			puts("program (name)");
+			break;
+		}
+		case NODE_IMPORT: {
+			printf("import %s\n", node->import.name);
+			break;
+		}
 		default: {
 			printf("unknown %d\n", node->i.type);
 			assert(0);
@@ -940,6 +977,28 @@ 			Parser_FreeNode(node->whileStat.condition);
 			FreeNodes(node->whileStat.body, node->whileStat.bodyLen);
 			break;
 		}
+		case NODE_TYPE_DEF: {
+			free(node->typeDef.name);
+			Parser_FreeNode(node->typeDef.type);
+			break;
+		}
+		case NODE_TYPE_BLOCK: {
+			free(node->typeBlock.name);
+
+			for (size_t i = 0; i < node->typeBlock.fieldsNum; ++ i) {
+				free(node->typeBlock.fields[i].name);
+				Parser_FreeNode(node->typeBlock.fields[i].type);
+			}
+
+			if (node->typeBlock.fields) {
+				free(node->typeBlock.fields);
+			}
+			break;
+		}
+		case NODE_IMPORT: {
+			free(node->import.name);
+			break;
+		}
 		default: return;
 	}
 }
@@ -961,6 +1020,8 @@ 		case NODE_IF:         return "if";
 		case NODE_WHILE:      return "while";
 		case NODE_TYPE_DEF:   return "type def";
 		case NODE_TYPE_BLOCK: return "type block";
+		case NODE_PROGRAM:    return "program";
+		case NODE_IMPORT:     return "import";
 		default:              return "???";
 	}
 }




diff --git a/basic/source/parser.h b/basic/source/parser.h
index afda080435be4578eba425dc8401b6b0c203504e..0bb03b42b3ad8fce6e3750b92757feabd3ccfe04 100644
--- a/basic/source/parser.h
+++ b/basic/source/parser.h
@@ -20,7 +20,9 @@ 	NODE_EXTERN,
 	NODE_IF,
 	NODE_WHILE,
 	NODE_TYPE_DEF,
-	NODE_TYPE_BLOCK
+	NODE_TYPE_BLOCK,
+	NODE_PROGRAM,
+	NODE_IMPORT
 } NodeType;
 
 typedef struct {
@@ -135,6 +137,11 @@ 	TypeBlockField* fields;
 	size_t          fieldsNum;
 } TypeBlockNode;
 
+typedef struct {
+	NodeInfo i;
+	char*    name;
+} ImportNode;
+
 union Node {
 	NodeInfo       i;
 	IntNode        integer;
@@ -151,6 +158,7 @@ 	IfNode         ifStat;
 	WhileNode      whileStat;
 	TypeDefNode    typeDef;
 	TypeBlockNode  typeBlock;
+	ImportNode     import;
 };
 
 typedef struct {




diff --git a/basic/source/semanticAnalysis.c b/basic/source/semanticAnalysis.c
index f3c6326aed9182df120d911c44d66cb35c876fce..86d59ee8a3f4ac1c0555a6796bddfa5a330780b7 100644
--- a/basic/source/semanticAnalysis.c
+++ b/basic/source/semanticAnalysis.c
@@ -495,6 +495,8 @@
 			State_AddType(newType);
 			return GET_PRIM("unit", 0);
 		}
+		case NODE_PROGRAM: return GET_PRIM("unit", 0);
+		case NODE_IMPORT:  return GET_PRIM("unit", 0);
 		default: {
 			Unexpected(node);
 		}




diff --git a/basic/source/state.c b/basic/source/state.c
index cbc1d5d22a58a42866e7bbc3fe35afe4afd1afb9..216160a16844058fa5187d8339e8479fa6181406 100644
--- a/basic/source/state.c
+++ b/basic/source/state.c
@@ -3,6 +3,8 @@ #include 
 #include "mem.h"
 #include "util.h"
 #include "state.h"
+#include "parser.h"
+#include "semanticAnalysis.h"
 
 State state;
 
@@ -141,3 +143,26 @@ 	return (type.type == TYPE_PRIM) && (
 		(type.primType == PRIM_UINT) || (type.primType == PRIM_INT)
 	);
 }
+
+bool State_ImportFile(const char* path) {
+	FILE* file = fopen(path, "r");
+
+	if (!file) {
+		return false;
+	}
+
+	Parser parser = Parser_Init(file, path, false);
+
+	size_t len;
+	Node*  nodes = Parser_Parse(&parser, &len);
+
+	for (size_t i = 0; i < len; ++ i) {
+		SemanticAnalysis_EvalType(&nodes[i]);
+	}
+
+	for (size_t i = 0; i < len; ++ i) {
+		Parser_FreeNode(&nodes[i]);
+	}
+	free(nodes);
+	return true;
+}




diff --git a/basic/source/state.h b/basic/source/state.h
index a52b2bed4cd11062a5743e1852a08a80ed514053..decf9f57253ba9f349bcc40562a1dd26754b6492 100644
--- a/basic/source/state.h
+++ b/basic/source/state.h
@@ -83,5 +83,6 @@ bool      State_UsedTypeEq(UsedType a, UsedType b);
 void      State_DumpInfo(void);
 Type*     State_GetTypeFromUsed(UsedType type);
 bool      State_IsInt(Type type);
+bool      State_ImportFile(const char* path);
 
 #endif




diff --git a/basic/source/util.c b/basic/source/util.c
index 3524bb0b4c307b7a93a3b07c1c2c13fe438d9956..329b7cd160d0dcaa8ce7a5298d49f6faf6540d44 100644
--- a/basic/source/util.c
+++ b/basic/source/util.c
@@ -1,3 +1,4 @@
+#include <stdio.h>
 #include <string.h>
 #include "mem.h"
 #include "common.h"
@@ -14,3 +15,28 @@ 	strcpy(ret, first);
 	strcat(ret, second);
 	return ret;
 }
+
+bool FileExists(const char* name) {
+	FILE* file = fopen(name, "r");
+
+	if (!file) {
+		return false;
+	}
+
+	fclose(file);
+	return true;
+}
+
+char* DirName(const char* name) {
+	char* ret   = NewString(name);
+	char* slash = strchr(ret, '/');
+
+	if (slash) {
+		*slash = 0;
+		return ret;
+	}
+	else {
+		free(ret);
+		return NewString(".");
+	}
+}




diff --git a/basic/source/util.h b/basic/source/util.h
index aa57880a037cf533331c0e4cdedfcef4953b4465..549ac204e7c3d0d2a4d6e6ecd968840c3b369b96 100644
--- a/basic/source/util.h
+++ b/basic/source/util.h
@@ -1,7 +1,7 @@
 #ifndef N_UTIL_H
 #define N_UTIL_H
 
-#include <stdio.h>
+#include "common.h"
 
 #define FUNCTION_POINTER(TYPE, NAME, ...) TYPE (*NAME)(__VA_ARGS__)
 #define MIN(A, B) (((A) < (B))? (A) : (B))
@@ -18,5 +18,7 @@ 	} while (0);
 
 char* NewString(const char* src);
 char* ConcatString(const char* first, const char* second);
+bool  FileExists(const char* name);
+char* DirName(const char* name);
 
 #endif




diff --git a/basic/test.bas b/basic/test.bas
index a5731efea260b2d45a679be8a4c84801d8f33ac6..532d91881fffa19498acf9852c9bc48d1077876e 100644
--- a/basic/test.bas
+++ b/basic/test.bas
@@ -1,16 +1,10 @@
 'NITRON
 
+program test
+
 extern func puts(str as ptr(char)) i32
 extern func printf(str as ptr(char), val as int) i32
 
-dim foo as int
-foo = 5
-
-dim fooPtr as ptr(int)
-fooPtr = &foo
-
-printf("foo = %d\n", ^fooPtr)
+import lib
 
-^fooPtr = 6
-
-printf("foo = %d\n", foo)
+PrintStrLn("hello world!")