Skip to content

Typescript like syntax

What's already done in this project is very nice !

And I think if "lily" allowed a "typescript" like syntax more users would be attracted to use it, we could reuse a lot of already existing code and with some care 'lily' code could also be used by "typescritp"/"javascript".

In my cloned repository I did some small experimental changes to get closer to "typescript" syntax, I'm pondering fork "lily" and try it but before doing it I would like to know the opinion of others.

With the changes bellow we can have:

  • C like comments
  • Use 'function' or 'define' to create functions
  • Use 'let' or 'var' to create variables
  • Use 'throw' or 'raise' to throw exceptions
  • Use 'catch' or 'except' to catch exceptions
  • Use 'const' or 'constant' to define constants
  • Use 'extends' or '<' to extend a class
--------------------------- scripts/parser_data.lily ---------------------------
index 2a8bfa8a..0f888efc 100644
@@ -29,7 +29,9 @@ var constants = [
 var keywords = [
     "break",
     "case",
+    "catch",
     "class",
+    "const",
     "constant",
     "continue",
     "define",
@@ -38,10 +40,13 @@ var keywords = [
     "else",
     "enum",
     "except",
+    "extends",
     "for",
     "forward",
+    "function",
     "if",
     "import",
+    "let",
     "match",
     "private",
     "protected",
@@ -50,6 +55,8 @@ var keywords = [
     "return",
     "scoped",
     "static",
+    "struct",
+    "throw",
     "try",
     "var",
     "while",
@@ -207,6 +214,15 @@ define write_define_table(f: File, header: String,
     }
 
     f.write("\n")
+
+    for i in 0...names.size() - 1: {
+        var n = names[i]
+        var rec = "# define KEYWORD_" ++ n ++ " \"" ++ n.lower() ++ "\"\n"
+
+        f.write(rec)
+    }
+
+    f.write("\n")
 }
 
 define write_expression_handlers(f: File)

------------------------------- src/lily_lexer.c -------------------------------
index aaae841a..9364c0a4 100644
@@ -595,7 +595,7 @@ static void scan_number(lily_lex_state *lex, char **source_ch, uint8_t *tok)
     *source_ch = ch;
 }
 
-static void scan_multiline_comment(lily_lex_state *lex, char **source_ch)
+static void scan_multiline_comment_base(lily_lex_state *lex, char **source_ch, char chr_close1, char chr_close2)
 {
     lex->expand_start_line = lex->line_num;
 
@@ -603,8 +603,8 @@ static void scan_multiline_comment(lily_lex_state *lex, char **source_ch)
     char *ch = *source_ch + 1;
 
     while (1) {
-        if (*ch == ']' &&
-            *(ch + 1) == '#') {
+        if (*ch == chr_close1 &&
+            *(ch + 1) == chr_close2) {
             ch += 2;
             break;
         }
@@ -626,6 +626,11 @@ static void scan_multiline_comment(lily_lex_state *lex, char **source_ch)
     *source_ch = ch;
 }
 
+static void scan_multiline_comment(lily_lex_state *lex, char **source_ch)
+{
+    scan_multiline_comment_base(lex, source_ch, ']', '#');
+}
+
 static void check_label_size(lily_lex_state *lex, uint32_t at_least)
 {
     if (lex->label_size > at_least)
@@ -642,7 +647,7 @@ static void check_label_size(lily_lex_state *lex, uint32_t at_least)
     lex->label_size = new_size;
 }
 
-static void scan_docblock(lily_lex_state *lex, char **source_ch)
+static void scan_docblock_base(lily_lex_state *lex, char **source_ch, char chr1, char chr2, char chr3)
 {
     uint16_t offset = (uint16_t)(*source_ch - lex->source);
     uint16_t start_line = lex->line_num;
@@ -658,15 +663,15 @@ static void scan_docblock(lily_lex_state *lex, char **source_ch)
             i++;
         }
 
-        if (*ch != '#') {
+        if (*ch != chr1) {
             if (lex->line_num == start_line)
                 lily_raise_syn(lex->raiser,
                         "Docblock is preceded by non-whitespace.");
 
             break;
         }
-        else if (*(ch + 1) != '#' ||
-                 *(ch + 2) != '#')
+        else if (*(ch + 1) != chr2 ||
+                 *(ch + 2) != chr3)
             lily_raise_syn(lex->raiser,
                     "Docblock line does not start with full '###'.");
         else if (i != offset)
@@ -704,6 +709,11 @@ static void scan_docblock(lily_lex_state *lex, char **source_ch)
     label[label_pos] = '\0';
 }
 
+static void scan_docblock(lily_lex_state *lex, char **source_ch)
+{
+    scan_docblock_base(lex, source_ch, '#', '#', '#');
+}
+
 /* Scan a String or ByteString literal. This starts on the cursor provided and
    updates it.
    The caller is expected to set the cursor on the earliest part of the literal.
@@ -1372,6 +1382,20 @@ word_case: ;
         /* These five tokens have their assign as +1 from their base. */
         case tk_bitwise_xor:
         case tk_divide:
+            if (*(ch + 1) == '/') {
+                if (*(ch + 2) == '#') {
+                    scan_docblock_base(lex, &ch, '/', '/', '#');
+                    token = tk_docblock;
+                    break;                    
+                }
+                else read_line(lex);
+                goto start;
+            }
+            else if (*(ch + 1) == '*') {
+                scan_multiline_comment_base(lex, &ch, '*', '/');
+                lex->read_cursor = ch;
+                goto start;
+            }
         case tk_not:
         case tk_modulo:
         case tk_multiply:

------------------------------ src/lily_parser.c ------------------------------
index b0041266..249a338b 100644
@@ -2673,8 +2673,17 @@ static int keyword_by_name(const char *name)
 
     for (i = 0;i < KEY_BAD_ID;i++) {
         if (keywords[i].shorthash == shorthash &&
-            strcmp(keywords[i].name, name) == 0)
+            strcmp(keywords[i].name, name) == 0) {
+            switch(i) {
+                case KEY_FUNCTION:
+                    return KEY_DEFINE;
+                case KEY_LET:
+                    return KEY_VAR;
+                default:
+                    return i;
+            }
             return i;
+        }
         else if (keywords[i].shorthash > shorthash)
             break;
     }
@@ -3795,6 +3804,11 @@ static void error_forward_decl_keyword(lily_parse_state *parser, int key)
     lily_raise_syn(parser->raiser, lily_mb_raw(msgbuf));
 }
 
+static void keyword_let(lily_parse_state *parser)
+{
+    keyword_var(parser);
+}
+
 static void keyword_var(lily_parse_state *parser)
 {
     lily_lex_state *lex = parser->lex;
@@ -4096,6 +4110,11 @@ static void parse_one_constant(lily_parse_state *parser)
     lily_next_token(lex);
 }
 
+static void keyword_const(lily_parse_state *parser)
+{
+    keyword_const(parser);
+}
+
 static void keyword_constant(lily_parse_state *parser)
 {
     lily_lex_state *lex = parser->lex;
@@ -4496,6 +4515,11 @@ static void keyword_try(lily_parse_state *parser)
     lily_next_token(lex);
 }
 
+static void keyword_catch(lily_parse_state *parser)
+{
+    keyword_except(parser);
+}
+
 static void keyword_except(lily_parse_state *parser)
 {
     lily_emit_state *emit = parser->emit;
@@ -4529,6 +4553,11 @@ static void keyword_except(lily_parse_state *parser)
     NEED_COLON_AND_NEXT;
 }
 
+static void keyword_throw(lily_parse_state *parser)
+{
+    keyword_raise(parser);
+}
+
 static void keyword_raise(lily_parse_state *parser)
 {
     if (parser->emit->scope_block->block_type == block_lambda)
@@ -4703,7 +4732,7 @@ static void parse_class_header(lily_parse_state *parser, lily_class *cls)
     lily_tm_add(parser->tm, cls->self_type);
     collect_call_args(parser, call_var, F_COLLECT_CLASS);
 
-    if (lex->token == tk_lt)
+    if (lex->token == tk_lt || (lex->token == tk_word && strcmp(lex->label, KEYWORD_EXTENDS) == 0))
         parse_super(parser, cls);
 
     /* Don't make 'self' available until the class is fully constructed. */
@@ -4831,6 +4860,16 @@ static void parse_forward_class_body(lily_parse_state *parser, lily_class *cls)
     cls->flags |= SYM_IS_FORWARD;
 }
 
+static void keyword_extends(lily_parse_state *parser)
+{
+    //do nothing
+}
+
+static void keyword_struct(lily_parse_state *parser)
+{
+    keyword_class(parser);
+}
+
 static void keyword_class(lily_parse_state *parser)
 {
     lily_block *block = parser->emit->block;
@@ -4951,7 +4990,7 @@ static void enum_method_check(lily_parse_state *parser)
     if (lex->token == tk_right_curly)
         ;
     else if (lex->token == tk_word &&
-             strcmp(lex->label, "define") == 0) {
+             strcmp(lex->label, KEYWORD_DEFINE) == 0) {
         lily_next_token(lex);
         keyword_define(parser);
     }
@@ -5351,6 +5390,11 @@ static lily_var *parse_new_define(lily_parse_state *parser, lily_class *parent,
 
 #define ALLOW_DEFINE (SCOPE_CLASS | SCOPE_DEFINE | SCOPE_ENUM | SCOPE_FILE)
 
+static void keyword_function(lily_parse_state *parser)
+{
+    keyword_define(parser);
+}
+
 static void keyword_define(lily_parse_state *parser)
 {
     lily_block *block = parser->emit->block;
@@ -5855,7 +5899,7 @@ static void manifest_foreign(lily_parse_state *parser)
 
     lily_next_token(lex);
 
-    if (lex->token == tk_word && strcmp(lex->label, "static") == 0) {
+    if (lex->token == tk_word && strcmp(lex->label, KEYWORD_STATIC) == 0) {
         is_static = 1;
         lily_next_token(lex);
     }

---------------------------- src/lily_parser_data.h ----------------------------
index bac162b0..e1a0a6ec 100644
@@ -33,12 +33,23 @@ keyword_entry constants[] =
 # define CONST___FUNCTION__ 7
 # define CONST_BAD_ID 8
 
+# define KEYWORD_TRUE "true"
+# define KEYWORD_SELF "self"
+# define KEYWORD_UNIT "unit"
+# define KEYWORD_FALSE "false"
+# define KEYWORD___DIR__ "__dir__"
+# define KEYWORD___FILE__ "__file__"
+# define KEYWORD___LINE__ "__line__"
+# define KEYWORD___FUNCTION__ "__function__"
+# define KEYWORD_BAD_ID "bad_id"
+
 keyword_entry keywords[] =
 {
     {"if", 26217},
     {"do", 28516},
     {"var", 7496054},
     {"for", 7499622},
+    {"let", 7628140},
     {"try", 7959156},
     {"case", 1702060387},
     {"else", 1702063205},
@@ -47,20 +58,26 @@ keyword_entry keywords[] =
     {"enum", 1836412517},
     {"while", 435610544247},
     {"raise", 435727982962},
+    {"catch", 448345170275},
     {"match", 448345170285},
     {"break", 461195539042},
     {"class", 495857003619},
+    {"const", 500152823651},
+    {"throw", 512970877044},
     {"public", 109304441107824},
     {"static", 109304575259763},
     {"scoped", 110386840822643},
     {"define", 111524889126244},
     {"return", 121437875889522},
+    {"struct", 127970521019507},
     {"except", 128026086176869},
     {"import", 128034844732777},
     {"forward", 28273260612448102},
     {"private", 28556934595048048},
+    {"extends", 32480047633037413},
     {"protected", 7310577382525465200},
     {"continue", 7310870969309884259},
+    {"function", 7957695015192261990},
     {"constant", 8389750308618530659},
 };
 
@@ -68,30 +85,73 @@ keyword_entry keywords[] =
 # define KEY_DO 1
 # define KEY_VAR 2
 # define KEY_FOR 3
-# define KEY_TRY 4
-# define KEY_CASE 5
-# define KEY_ELSE 6
-# define KEY_ELIF 7
-# define KEY_WITH 8
-# define KEY_ENUM 9
-# define KEY_WHILE 10
-# define KEY_RAISE 11
-# define KEY_MATCH 12
-# define KEY_BREAK 13
-# define KEY_CLASS 14
-# define KEY_PUBLIC 15
-# define KEY_STATIC 16
-# define KEY_SCOPED 17
-# define KEY_DEFINE 18
-# define KEY_RETURN 19
-# define KEY_EXCEPT 20
-# define KEY_IMPORT 21
-# define KEY_FORWARD 22
-# define KEY_PRIVATE 23
-# define KEY_PROTECTED 24
-# define KEY_CONTINUE 25
-# define KEY_CONSTANT 26
-# define KEY_BAD_ID 27
+# define KEY_LET 4
+# define KEY_TRY 5
+# define KEY_CASE 6
+# define KEY_ELSE 7
+# define KEY_ELIF 8
+# define KEY_WITH 9
+# define KEY_ENUM 10
+# define KEY_WHILE 11
+# define KEY_RAISE 12
+# define KEY_CATCH 13
+# define KEY_MATCH 14
+# define KEY_BREAK 15
+# define KEY_CLASS 16
+# define KEY_CONST 17
+# define KEY_THROW 18
+# define KEY_PUBLIC 19
+# define KEY_STATIC 20
+# define KEY_SCOPED 21
+# define KEY_DEFINE 22
+# define KEY_RETURN 23
+# define KEY_STRUCT 24
+# define KEY_EXCEPT 25
+# define KEY_IMPORT 26
+# define KEY_FORWARD 27
+# define KEY_PRIVATE 28
+# define KEY_EXTENDS 29
+# define KEY_PROTECTED 30
+# define KEY_CONTINUE 31
+# define KEY_FUNCTION 32
+# define KEY_CONSTANT 33
+# define KEY_BAD_ID 34
+
+# define KEYWORD_IF "if"
+# define KEYWORD_DO "do"
+# define KEYWORD_VAR "var"
+# define KEYWORD_FOR "for"
+# define KEYWORD_LET "let"
+# define KEYWORD_TRY "try"
+# define KEYWORD_CASE "case"
+# define KEYWORD_ELSE "else"
+# define KEYWORD_ELIF "elif"
+# define KEYWORD_WITH "with"
+# define KEYWORD_ENUM "enum"
+# define KEYWORD_WHILE "while"
+# define KEYWORD_RAISE "raise"
+# define KEYWORD_CATCH "catch"
+# define KEYWORD_MATCH "match"
+# define KEYWORD_BREAK "break"
+# define KEYWORD_CLASS "class"
+# define KEYWORD_CONST "const"
+# define KEYWORD_THROW "throw"
+# define KEYWORD_PUBLIC "public"
+# define KEYWORD_STATIC "static"
+# define KEYWORD_SCOPED "scoped"
+# define KEYWORD_DEFINE "define"
+# define KEYWORD_RETURN "return"
+# define KEYWORD_STRUCT "struct"
+# define KEYWORD_EXCEPT "except"
+# define KEYWORD_IMPORT "import"
+# define KEYWORD_FORWARD "forward"
+# define KEYWORD_PRIVATE "private"
+# define KEYWORD_EXTENDS "extends"
+# define KEYWORD_PROTECTED "protected"
+# define KEYWORD_CONTINUE "continue"
+# define KEYWORD_FUNCTION "function"
+# define KEYWORD_CONSTANT "constant"
+# define KEYWORD_BAD_ID "bad_id"
 
 static void expr_arrow(lily_parse_state *, uint16_t *);
 static void expr_binary(lily_parse_state *, uint16_t *);
@@ -184,6 +244,7 @@ static void keyword_if(lily_parse_state *);
 static void keyword_do(lily_parse_state *);
 static void keyword_var(lily_parse_state *);
 static void keyword_for(lily_parse_state *);
+static void keyword_let(lily_parse_state *);
 static void keyword_try(lily_parse_state *);
 static void keyword_case(lily_parse_state *);
 static void keyword_else(lily_parse_state *);
@@ -192,20 +253,26 @@ static void keyword_with(lily_parse_state *);
 static void keyword_enum(lily_parse_state *);
 static void keyword_while(lily_parse_state *);
 static void keyword_raise(lily_parse_state *);
+static void keyword_catch(lily_parse_state *);
 static void keyword_match(lily_parse_state *);
 static void keyword_break(lily_parse_state *);
 static void keyword_class(lily_parse_state *);
+static void keyword_const(lily_parse_state *);
+static void keyword_throw(lily_parse_state *);
 static void keyword_public(lily_parse_state *);
 static void keyword_static(lily_parse_state *);
 static void keyword_scoped(lily_parse_state *);
 static void keyword_define(lily_parse_state *);
 static void keyword_return(lily_parse_state *);
+static void keyword_struct(lily_parse_state *);
 static void keyword_except(lily_parse_state *);
 static void keyword_import(lily_parse_state *);
 static void keyword_forward(lily_parse_state *);
 static void keyword_private(lily_parse_state *);
+static void keyword_extends(lily_parse_state *);
 static void keyword_protected(lily_parse_state *);
 static void keyword_continue(lily_parse_state *);
+static void keyword_function(lily_parse_state *);
 static void keyword_constant(lily_parse_state *);
 
 static keyword_handler *handlers[] =
@@ -214,6 +281,7 @@ static keyword_handler *handlers[] =
     keyword_do,
     keyword_var,
     keyword_for,
+    keyword_let,
     keyword_try,
     keyword_case,
     keyword_else,
@@ -222,20 +290,26 @@ static keyword_handler *handlers[] =
     keyword_enum,
     keyword_while,
     keyword_raise,
+    keyword_catch,
     keyword_match,
     keyword_break,
     keyword_class,
+    keyword_const,
+    keyword_throw,
     keyword_public,
     keyword_static,
     keyword_scoped,
     keyword_define,
     keyword_return,
+    keyword_struct,
     keyword_except,
     keyword_import,
     keyword_forward,
     keyword_private,
+    keyword_extends,
     keyword_protected,
     keyword_continue,
+    keyword_function,
     keyword_constant,
 };
 

-------------------------- test/class/test_class.lily --------------------------
index 1cdb74e7..8a9b31bb 100644
@@ -26,6 +26,40 @@ class TestClass < TestCase
             if t.a != 10: { 0 / 0 }
         """)
 
+        assert_parse_string(t, """
+            class One {
+                public var @a = 10
+
+                public define increment { @a += 1 }
+            }
+
+            class Two extends One {
+                public var @b = "asdf"
+            }
+
+            var t = Two()
+
+            if t.b != "asdf": { 0 / 0 }
+            if t.a != 10: { 0 / 0 }
+        """)
+
+        assert_parse_string(t, """
+            struct One {
+                public var @a = 10
+
+                public define increment { @a += 1 }
+            }
+
+            struct Two extends One {
+                public var @b = "asdf"
+            }
+
+            var t = Two()
+
+            if t.b != "asdf": { 0 / 0 }
+            if t.a != 10: { 0 / 0 }
+        """)
+
         # basics (assign to inheriting class)
 
         assert_parse_string(t, """

------------------- test/constant/test_verify_constant.lily -------------------
index 7f6567e0..3138af28 100644
@@ -17,6 +17,14 @@ class TestVerifyConstant < TestCase
             }
         """)
 
+        assert_parse_string(t, """
+            const integer_a = 10
+
+            if integer_a != 10: {
+                0/0
+            }
+        """)
+
         # basics (multiple constants)
 
         assert_parse_string(t, """

---------------------- test/exception/test_exception.lily ----------------------
index a5a662de..9dc9b56e 100644
@@ -17,6 +17,14 @@ class TestExceptions < TestCase
             }
         """)
 
+        assert_parse_string(t, """
+            try: {
+                throw ValueError("")
+            catch ValueError as e:
+                var v = e.traceback[0].split(":")[-1]
+            }
+        """)
+
         # base (format check for error from vm)
 
         t = Interpreter()