Skip to content

Instantly share code, notes, and snippets.

@soda92
Created November 28, 2024 09:49
Show Gist options
  • Save soda92/9c5454f4ac97251a696f5478eb0667a2 to your computer and use it in GitHub Desktop.
Save soda92/9c5454f4ac97251a696f5478eb0667a2 to your computer and use it in GitHub Desktop.
how to add python-style decorator to my language

how to add python-style decorator to my language

I followed https://craftinginterpreters.com/functions.html and function call is implemented.

The interpreter can interpret block like this:

fun deco(func) {
  fun impl() {
    print "start" + func;
    func();
    print "end" + func;
  }
  return impl;
}

fun hello(){print "hello";}
var h2 = deco(hello);
h2();

with the result:

start<fn hello>
hello
end<fn hello>

However, as I recalled Python decorator pattern, which is more concise, I want to add @ as a syntax sugar.

So I did the following modifications (github commit)

--- a/src/main/java/com/craftinginterpreters/lox/Scanner.java
+++ b/src/main/java/com/craftinginterpreters/lox/Scanner.java
@@ -31,6 +31,7 @@ public class Scanner {
         keywords.put("true", TRUE);
         keywords.put("var", VAR);
         keywords.put("while", WHILE);
+        keywords.put("@", DECO);
     }
 
     Scanner(String source) {
@@ -65,6 +66,7 @@ public class Scanner {
             case '+' -> addToken(PLUS);
             case ';' -> addToken(SEMICOLON);
             case '*' -> addToken(STAR);
+            case '@' -> addToken(DECO);
             case '!' -> addToken(match('=') ? BANG_EQUAL : BANG);
             case '=' -> addToken(match('=') ? EQUAL_EQUAL : EQUAL);
             case '<' -> addToken(match('=') ? LESS_EQUAL : LESS);

--- a/src/main/java/com/craftinginterpreters/lox/Parser.java
+++ b/src/main/java/com/craftinginterpreters/lox/Parser.java
@@ -25,6 +25,7 @@ public class Parser {
         try {
             if (match(FUN)) return function("function");
             if (match(VAR)) return varDeclaration();
+            if (match(DECO)) return decoDecl();
             return statement();
         } catch (ParseError error) {
             synchonize();
@@ -32,19 +33,32 @@ public class Parser {
         }
     }
 
+    private Stmt decoDecl() {
+        Token deco_method = consume(IDENTIFIER, "expect deco name");
+        consume(FUN, "expect function after decorator");
+        Stmt.Function e2 = function("function");
+        Expr e3 = new Expr.Call(
+            new Expr.Variable(deco_method),
+             null, 
+             Arrays.asList(
+                new Expr.Variable(e2.name)
+            )
+        );
+        return new Stmt.Var(e2.name, e3);
+    }
+

However, I don't know how to put the previous function declarations.

So it only works like this:

fun deco(func) {
  fun impl() {
    print "start" + func;
    func();
    print "end" + func;
  }
  return impl;
}


fun hello(){print "hello";}  
^--- If removed this line the interpreter cannot found variable `hello`

@deco
fun hello(){print "hello";}

hello();

I tried using Stmt.Block but since it creates a new scope the function declaration is discarded after exited the scope.

Any Suggestions?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment