diff --git a/code_obj.lua b/code_obj.lua
index a09608f..a31c4aa 100644
--- a/code_obj.lua
+++ b/code_obj.lua
@@ -105,6 +105,35 @@ do
       end
       return self.__str
     end,
+    last = function(self, n)
+      if self.__str then
+        return self.__str:sub(-n, -1)
+      end
+      local last = ""
+      for i = #self.bits, 1, -1 do
+        local b = self.bits[i]
+        last = (type(b) == 'string' and b:sub(-(n - #last)) or b:last(n - #last)) .. last
+        if #last == n then
+          break
+        end
+      end
+      return last
+    end,
+    first = function(self, n)
+      if self.__str then
+        return self.__str:sub(1, n)
+      end
+      local first = ""
+      local _list_0 = self.bits
+      for _index_0 = 1, #_list_0 do
+        local b = _list_0[_index_0]
+        first = first .. (type(b) == 'string' and b:sub(1, n - #first + 1) or b:first(n - #first + 1))
+        if #first == n then
+          break
+        end
+      end
+      return first
+    end,
     __tostring = function(self)
       return self:text()
     end,
@@ -139,7 +168,16 @@ do
       end
     end,
     __len = function(self)
-      return #self:text()
+      if self.__str then
+        return #self.__str
+      end
+      local len = 0
+      local _list_0 = self.bits
+      for _index_0 = 1, #_list_0 do
+        local b = _list_0[_index_0]
+        len = len + #b
+      end
+      return len
     end,
     match = function(self, ...)
       return self:text():match(...)
@@ -222,10 +260,7 @@ do
         if type(b) ~= 'string' then
           b.dirty = error
         end
-        if not (type(b) == 'string') then
-          b = b:text()
-        end
-        local line = match(b, "\n([^\n]*)$")
+        local line = b:match("\n([^\n]*)$")
         if line then
           line_len = #line
         else
@@ -404,7 +439,6 @@ do
             end
           else
             walk(b, pos)
-            b = b:text()
           end
           pos = pos + #b
         end
diff --git a/code_obj.moon b/code_obj.moon
index 9315264..3620676 100644
--- a/code_obj.moon
+++ b/code_obj.moon
@@ -71,6 +71,25 @@ class Code
             @__str = concat(buff, "")
         return @__str
 
+    last: (n)=>
+        if @__str
+            return @__str\sub(-n, -1)
+        last = ""
+        for i=#@bits,1,-1
+            b = @bits[i]
+            last = (type(b) == 'string' and b\sub(-(n-#last)) or b\last(n-#last))..last
+            break if #last == n
+        return last
+
+    first: (n)=>
+        if @__str
+            return @__str\sub(1,n)
+        first = ""
+        for b in *@bits
+            first ..= type(b) == 'string' and b\sub(1,n-#first+1) or b\first(n-#first+1)
+            break if #first == n
+        return first
+
     __tostring: => @text!
 
     as_lua: =>
@@ -79,7 +98,13 @@ class Code
         else
             "#{@__class.__name}(#{concat [b\as_lua! for b in *@bits], ", "})"
 
-    __len: => #@text!
+    __len: =>
+        if @__str
+            return #@__str
+        len = 0
+        for b in *@bits
+            len += #b
+        return len
     
     match: (...)=> @text!\match(...)
 
@@ -137,9 +162,7 @@ class Code
                     bits[#bits+1] = joiner
             bits[#bits+1] = b
             b.dirty = error if type(b) != 'string'
-            unless type(b) == 'string'
-                b = b\text!
-            line = match(b, "\n([^\n]*)$")
+            line = b\match("\n([^\n]*)$")
             if line
                 line_len = #line
             else
@@ -231,7 +254,6 @@ class LuaCode extends Code
                         nomsu_to_lua[lua.source.start] = pos
                 else
                     walk b, pos
-                    b = b\text!
                 pos += #b
         walk self, 1
         return {