diff --git a/examples/how_do_i.nom b/examples/how_do_i.nom
index 486ea19..2365627 100644
--- a/examples/how_do_i.nom
+++ b/examples/how_do_i.nom
@@ -49,7 +49,7 @@ say "Hello world!"
 # Use a list?
 %my_list = ["first item", "second item", "third item"]
 # Lists are 1-indexed because they're implemented as Lua tables, so this prints "first item"
-say (%my_list -> 1)
+say (%my_list->1)
 # These do the same thing:
 say (%my_list's 1)
 say (1 in %my_list)
@@ -58,7 +58,12 @@ say (1 in %my_list)
 say (size of %my_list)
 
 # Define a dictionary/hash map?
-# Dicts are created by passing a list of key-value pairs to the function "dict"
+%my_dict = (dict {x = 99; y = 101})
+%my_dict = (..)
+    dict:
+        x = 101
+        "99 bottles" = 99
+        653 = 292
 %my_dict = (dict [["x", 99], ["y", 101]])
 
 # Use a dict?
@@ -175,11 +180,11 @@ say (first fibonacci above 10)
 
 # Functions can have aliases, which may or may not have the arguments in different order
 rule [..]
-    I hate %worse-things more than %better-things
-    I think %worse-things are worse than %better-things
-    I like %better-things more than %worse-things
+    I hate %worse_things more than %better_things
+    I think %worse_things are worse than %better_things
+    I like %better_things more than %worse_things
 ..=:
-    say "\(%better-things capitalized) rule and \(%worse-things) drool!"
+    say "\(%better_things capitalized) rule and \(%worse_things) drool!"
 
 I like "dogs" more than "cats"
 I think "chihuahuas" are worse than "corgis"
@@ -190,20 +195,26 @@ I think "chihuahuas" are worse than "corgis"
 say both "Hello" and also "again!"
 
 # Functions can even have their name at the end:
-rule [%what-she-said is what she said] =:
-    say %what-she-said
+rule [%what_she_said is what she said] =:
+    say %what_she_said
     say "-- she said"
 
 "Howdy pardner" is what she said
 
-#.. The language only reserves []{}().,:;% as special characters, so functions and variables
+#.. The language only reserves []{}().,:;% as special characters, so functions
     can have really funky names!
-rule [>> %foo-bar$$$^ --> %@@& _~-^-~_~-^ %1 !] =:
-    say %foo-bar$$$^
-    say %@@&
+rule [>> %foo_bar $$$^ --> %@ @& _~-^-~_~-^ %1 !] =:
+    say %foo_bar
+    say %@
     say %1
 
->> "wow" --> "so flexible!" _~-^-~_~-^ "even numbers can be variables!" !
+>> "wow" $$$^ --> "so flexible!" @& _~-^-~_~-^ "even numbers can be variables!" !
+
+#.. The all of the following are characters won't "stick" to their neighbors, so the
+    compiler treats them as solitary single-character tokens: '~`!@$^&*-+=|<>?/
+    which means you can jam things together:
+rule [%x++%y] =: 2*(%x+%y)
+(5++2) == (   5   ++   2   )
 
 # Math and logic operations are just treated the same as function calls in the syntax
 say (2 + 3)
diff --git a/examples/sample_game.nom b/examples/sample_game.nom
index 3698fb4..a6a792d 100644
--- a/examples/sample_game.nom
+++ b/examples/sample_game.nom
@@ -30,7 +30,7 @@ with secrets:
             |    return t;
             |end});
 
-    rule [inventory] =: dict:
+    rule [inventory] =: dict (..)
         [%inv's "key", dict (%entry for %entry in (entries in (%inv's "value")))]
         ..for %inv in (entries in (secret %inventory))
 
@@ -207,16 +207,16 @@ propose:
         rule [vote for %candidate] =:
             for %c in (secret %candidates):
                 if (%c == %candidate):
-                    go to %candidate-is-legit
+                    go to %candidate_is_legit
             error ".."
                 |Invalid candidate: \(%candidate)
-            -> %candidate-is-legit
+            -> %candidate_is_legit
 
             (secret %votes)->(you) =: %candidate
-            %vote-percent = ((number of (votes for %candidate)) / (number of (everyone)))
+            %vote_percent = ((number of (votes for %candidate)) / (number of (everyone)))
             say ".."
-                |Vote cast. \(%candidate) now has \(100 * %vote-percent)% of the votes.
-            if (%vote-percent > 0.5):
+                |Vote cast. \(%candidate) now has \(100 * %vote_percent)% of the votes.
+            if (%vote_percent > 0.5):
                 secret %winner = %candidate
                 after winning a fair election do (secret %action)
                 close the election
diff --git a/lib/collections.nom b/lib/collections.nom
index 022c1dc..5958bb9 100644
--- a/lib/collections.nom
+++ b/lib/collections.nom
@@ -19,7 +19,7 @@ parse [first in %list, first %list] as: 1 st in %list
 parse [last in %list, last %list] as: 1 st to last in %list
 
 # Dict iteration convenience function. This could also be accomplished with: for all (entries in %dict): ...
-compile [for %key -> %value in %dict %body] to code: ".."
+compile [for %key = %value in %dict %body] to code: ".."
     |do;
     |    for k, v in pairs(\(%dict as lua)) do;
     |        \(%key as lua), \(%value as lua) = k, v;
@@ -29,7 +29,7 @@ compile [for %key -> %value in %dict %body] to code: ".."
 
 # Membership testing
 rule [%item is in %list, %list contains %item, %list has %item] =:
-    for %key -> %value in %list:
+    for %key = %value in %list:
         if (%key == %item): return (yes)
     return (no)
 
@@ -38,7 +38,7 @@ rule [..]
     %list doesn't contain %item, %list does not contain %item
     %list doesn't have %item, %list does not have %item
 ..=:
-    for %key -> %value in %list:
+    for %key = %value in %list:
         if (%key == %item): return (no)
     return (yes)
 
@@ -87,26 +87,53 @@ rule [flatten %lists] =:
             add %item to %flat
     %flat
 
-rule [dict %items] =:
+rule [dict from entries %items] =:
     %dict = []
     for %pair in %items:
         %dict -> (%pair -> 1) = (%pair -> 2)
     %dict
 
+compile [dict %items] to:
+    if ((%items's "type") == "Thunk"):
+        %item_codes = []
+        for %func_call in (%items's "value"):
+            assert ((%func_call's "type") == "FunctionCall") ".."
+                |Invalid format for 'dict' expression. Only literals are allowed.
+            %tokens = (%func_call's "value")
+            %equals = (%tokens -> 2)
+            assert (lua expr "#\(%tokens) == 3 and \(%equals) and \(%equals).type == 'Word' and \(%equals).value == '='") ".."
+                |Invalid format for 'dict' expression. Lines must only have the "% = %" format, not \(%func_call's "src")
+            %key = (%tokens -> 1)
+            lua code ".."
+                |if \(%key).type == "Word" and \(%key).value:match("^[a-zA-Z_][a-zA-Z0-9_]*$") then
+                |    \(%key_code) = \(%key).value;
+                |elseif \(%key).type == "Word" then
+                |    \(%key_code) = "["..nomsu:repr(\(%key).value).."]";
+                |else
+                |    \(\{%key_code = "[\((%key as lua))]"} as lua statements)
+                |end
+            add "\(%key_code) = \((%tokens -> 3) as lua)" to %item_codes
+        return "{\(join %item_codes with glue ",\n")}"
+    ..else:
+        return (..)
+            (..)
+                nomsu "replaced_vars" [\(dict from entries %items), lua expr "vars"]
+            ..as lua
+
 rule [entries in %dict] =:
     %entries = []
-    for %k -> %v in %dict:
-        add (dict [["key",%k],["value",%v]]) to %entries
+    for %k = %v in %dict:
+        add (dict {key = %k; value = %v}) to %entries
     %entries
 
 rule [keys in %dict] =:
     %keys = []
-    for %k -> %v in %dict: add %k to %keys
+    for %k = %v in %dict: add %k to %keys
     %keys
 
 rule [values in %dict] =:
     %values = []
-    for %k -> %v in %dict: add %v to %values
+    for %k = %v in %dict: add %v to %values
     %values
 
 # List Comprehension
diff --git a/lib/control_flow.nom b/lib/control_flow.nom
index 2c51c50..84aca6c 100644
--- a/lib/control_flow.nom
+++ b/lib/control_flow.nom
@@ -21,7 +21,7 @@ compile [if %condition %if_body else %else_body, unless %condition %else_body el
 
 # Return
 compile [return] to code: "do; return; end;"
-compile [return %return-value] to code: "do; return \(%return-value as lua); end;"
+compile [return %return_value] to code: "do; return \(%return_value as lua); end;"
 
 # GOTOs
 compile [-> %label] to code: ".."
@@ -44,11 +44,11 @@ rule [tree %tree has function call %call] =:
 compile [do next repeat-loop] to code: "goto continue_repeat;"
 compile [stop repeat-loop] to code: "goto stop_repeat;"
 compile [repeat while %condition %body] to code:
-    %continue-labels = (..)
+    %continue_labels = (..)
         "\n::continue_repeat::;" if (tree %body has function call \(do next repeat-loop)) else ""
     %code = ".."
         |while \(%condition as lua) do;
-        |\(%body as lua statements)\(%continue-labels)
+        |\(%body as lua statements)\(%continue_labels)
         |end;--while-loop
     if (tree %body has function call \(stop repeat-loop)):
         return ".."
@@ -73,25 +73,25 @@ compile [..]
     for %var from %start to %stop by %step %body
     for %var from %start to %stop via %step %body
 ..to code:
-    %continue-labels = ""
+    %continue_labels = ""
     if (tree %body has function call \(do next for-loop)):
-        %continue-labels join= "\n::continue_for::;"
+        %continue_labels join= "\n::continue_for::;"
     if (tree %body has function call (nomsu "replaced_vars" [\(do next %), lua expr "{['']=\(%var)}"])):
-        %continue-labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
+        %continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
     %code = ".."
         |for i=\(%start as lua),\(%stop as lua),\(%step as lua) do;
         # This trashes the loop variables, just like in Python.
         |\(%var as lua) = i;
-        |\(%body as lua statements)\(%continue-labels)
+        |\(%body as lua statements)\(%continue_labels)
         |end;--numeric for-loop
-    %stop-labels = ""
+    %stop_labels = ""
     if (tree %body has function call \(stop for-loop)):
-        %stop-labels join= "\n::stop_for::;"
+        %stop_labels join= "\n::stop_for::;"
     if (tree %body has function call (nomsu "replaced_vars" [\(stop %), lua expr "{['']=\(%var)}"])):
-        %stop-labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;"
-    if (%stop-labels != ""): ".."
+        %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;"
+    if (%stop_labels != ""): ".."
         |do;--for-loop label scope
-        |\(%code)\(%stop-labels)
+        |\(%code)\(%stop_labels)
         |end;--for-loop label scope
     ..else: %code
 parse [for %var from %start to %stop %body] as: for %var from %start to %stop via 1 %body
@@ -102,25 +102,25 @@ parse [..]
 parse [for all %start to %stop %body] as: for all %start to %stop via 1 %body
 
 compile [for %var in %iterable %body] to code:
-    %continue-labels = ""
+    %continue_labels = ""
     if (tree %body has function call \(do next for-loop)):
-        %continue-labels join= "\n::continue_for::;"
+        %continue_labels join= "\n::continue_for::;"
     if (tree %body has function call (nomsu "replaced_vars" [\(do next %), lua expr "{['']=\(%var)}"])):
-        %continue-labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
+        %continue_labels join= "\n::continue_\(nomsu "var_to_lua_identifier" [%var])::;"
     %code = ".."
         |for i,value in ipairs(\(%iterable as lua)) do;
         # This trashes the loop variables, just like in Python.
         |\(%var as lua) = value;
-        |\(%body as lua statements)\(%continue-labels)
+        |\(%body as lua statements)\(%continue_labels)
         |end;--foreach-loop
-    %stop-labels = ""
+    %stop_labels = ""
     if (tree %body has function call \(stop for-loop)):
-        %stop-labels join= "\n::stop_for::;"
+        %stop_labels join= "\n::stop_for::;"
     if (tree %body has function call (nomsu "replaced_vars" [\(stop %), lua expr "{['']=\(%var)}"])):
-        %stop-labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;"
-    if (%stop-labels != ""): ".."
+        %stop_labels join= "\n::stop_\(nomsu "var_to_lua_identifier" [%var])::;"
+    if (%stop_labels != ""): ".."
         |do;--for-loop label scope
-        |\(%code)\(%stop-labels)
+        |\(%code)\(%stop_labels)
         |end;--for-loop label scope
     ..else: %code
 parse [for all %iterable %body] as: for % in %iterable %body
@@ -131,10 +131,10 @@ compile [when %body] to code:
     %result = ""
     %fallthroughs = []
     %first = (yes)
-    for %func-call in (%body's "value"):
-        assert ((%func-call's "type") == "FunctionCall") ".."
+    for %func_call in (%body's "value"):
+        assert ((%func_call's "type") == "FunctionCall") ".."
             |Invalid format for 'when' statement. Only '*' blocks are allowed.
-        %tokens = (%func-call's "value")
+        %tokens = (%func_call's "value")
         %star = (%tokens -> 1)
         assert (lua expr "vars.star and vars.star.type == 'Word' and vars.star.value == '*'") ".."
             |Invalid format for 'when' statement. Lines must begin with '*'
@@ -146,7 +146,7 @@ compile [when %body] to code:
         %action = (%tokens -> 3)
         if (%action == (nil)):
             lua block "table.insert(vars.fallthroughs, vars.condition)"
-            do next %func-call
+            do next %func_call
 
         if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"):
             %result join= ".."
@@ -171,14 +171,14 @@ compile [when %body] to code:
     %result
 
 # Switch statement
-compile [when %branch-value == ? %body] to code:
+compile [when %branch_value == ? %body] to code:
     %result = ""
     %fallthroughs = []
     %first = (yes)
-    for %func-call in (%body's "value"):
-        assert ((%func-call's "type") == "FunctionCall") ".."
+    for %func_call in (%body's "value"):
+        assert ((%func_call's "type") == "FunctionCall") ".."
             |Invalid format for 'when' statement. Only '*' blocks are allowed.
-        %tokens = (%func-call's "value")
+        %tokens = (%func_call's "value")
         %star = (%tokens -> 1)
         assert (lua expr "vars.star and vars.star.type == 'Word' and vars.star.value == '*'") ".."
             |Invalid format for 'when' statement. Lines must begin with '*'
@@ -190,7 +190,7 @@ compile [when %branch-value == ? %body] to code:
         %action = (%tokens -> 3)
         if (%action == (nil)):
             lua block "table.insert(vars.fallthroughs, vars.condition)"
-            do next %func-call
+            do next %func_call
 
         if (lua expr "vars.condition.type == 'Word' and vars.condition.value == 'else'"):
             %result join= ".."
@@ -213,7 +213,7 @@ compile [when %branch-value == ? %body] to code:
     if (%result != ""):
         %result = ".."
             |do;--when == ?
-            |local branch_value = \(%branch-value as lua);\(%result)
+            |local branch_value = \(%branch_value as lua);\(%result)
             |end;
             |end;--when == ?
     %result
diff --git a/lib/operators.nom b/lib/operators.nom
index 13e6ce4..09bdbcd 100644
--- a/lib/operators.nom
+++ b/lib/operators.nom
@@ -29,7 +29,7 @@ compile [..]
     |end)(nomsu, vars)
 
 # Indexing:
-compile [%obj's %key, %obj -> %key] to: "(\(%obj as lua))[\(%key as lua)]"
+compile [%obj'%key, %obj's %key, %obj -> %key] to: "(\(%obj as lua))[\(%key as lua)]"
 
 # Variable assignment operator, and += type versions
 compile [%var = %val] to code: "\(%var as lua) = \(%val as lua);"
@@ -90,7 +90,7 @@ lua block ".."
 # Chained compairsions (e.g. x < y <= z) are defined up to 3 operands
 lua block ".."
     |local max_operands = 3;
-    |for _,chainers in ipairs({{"<","<="},{">",">="}}) do;
+    |for _,chainers in ipairs({{"<","< ="},{">","> ="}}) do;
     |    local function recurse(chainers, chain)
     # The 1-op versions are already more efficiently defined, and a 0-op version doesnt make sense
     |        if #chain >= 2 then;
diff --git a/lib/permissions.nom b/lib/permissions.nom
index 1725683..3231b25 100644
--- a/lib/permissions.nom
+++ b/lib/permissions.nom
@@ -15,33 +15,33 @@ rule [standardize rules %rules] =:
     keys in %set
 
 
-rule [restrict %rules to within %elite-rules] =:
+rule [restrict %rules to within %elite_rules] =:
     %rules = (standardize rules %rules)
-    %elite-rules = (standardize rules %elite-rules)
-    for all (flatten [%elite-rules, %rules]):
+    %elite_rules = (standardize rules %elite_rules)
+    for all (flatten [%elite_rules, %rules]):
         assert ((nomsu's "defs") has key %) "Undefined function: \(%)"
     for %rule in %rules:
         assert (nomsu "check_permission" [%]) ".."
             |You do not have permission to restrict permissions for function: \(%)
         ((nomsu) ->* ["defs",%rule,"whiteset"]) = (..)
-            dict ([%, yes] for all %elite-rules)
+            dict ([%, yes] for all %elite_rules)
 
-rule [allow %elite-rules to use %rules] =:
+rule [allow %elite_rules to use %rules] =:
     %rules = (standardize rules %rules)
-    %elite-rules = (standardize rules %elite-rules)
-    for all (flatten [%elite-rules, %rules]):
+    %elite_rules = (standardize rules %elite_rules)
+    for all (flatten [%elite_rules, %rules]):
         assert ((nomsu's "defs") has key %) "Undefined function: \(%)"
     for %rule in %rules:
         assert (nomsu "check_permission" [%rule]) ".."
             |You do not have permission to grant permissions for function: \(%rule)
         %whiteset = ((nomsu) ->* ["defs",%rule,"whiteset"])
         if (not %whiteset): go to next %rule
-        for all %elite-rules: %whiteset -> % = (yes)
+        for all %elite_rules: %whiteset -> % = (yes)
 
-rule [forbid %pleb-rules to use %rules] =:
+rule [forbid %pleb_rules to use %rules] =:
     %rules = (standardize rules %rules)
-    %pleb-rules = (standardize rules %pleb-rules)
-    for all (flatten [%pleb-rules, %used]):
+    %pleb_rules = (standardize rules %pleb_rules)
+    for all (flatten [%pleb_rules, %used]):
         assert ((nomsu's "defs") has key %) "Undefined function: \(%)"
     for all %rules:
         assert (nomsu "check_permission" [%]) ".."
@@ -50,4 +50,4 @@ rule [forbid %pleb-rules to use %rules] =:
         assert %whiteset ".."
             |Cannot individually restrict permissions for \(%) because it is currently
             |available to everyone. Perhaps you meant to use "restrict % to within %" instead?
-        for all %pleb-rules: %whiteset's % = (nil)
+        for all %pleb_rules: %whiteset's % = (nil)
diff --git a/lib/plurals.nom b/lib/plurals.nom
index 347c2e7..da0fa4d 100644
--- a/lib/plurals.nom
+++ b/lib/plurals.nom
@@ -29,7 +29,7 @@ with secrets:
     rule [plural %singular] =:
         %singular in (secret %plurals)
 
-    rule [canonicalize %item-name] =:
-        %item-name in (secret %canonicals)
+    rule [canonicalize %item_name] =:
+        %item_name in (secret %canonicals)
 
     rule [rules that change plurals] =: ["the plural of % is %"]
diff --git a/nomsu.lua b/nomsu.lua
index 28bb36d..cbcbf9e 100644
--- a/nomsu.lua
+++ b/nomsu.lua
@@ -70,7 +70,7 @@ local nomsu = [=[    file <- ({{| shebang?
     noeol_statement <- noeol_functioncall / noeol_expression
     inline_statement <- inline_functioncall / inline_expression
 
-    inline_thunk <- ({ {| "{" inline_statements "}" |} }) -> Thunk
+    inline_thunk <- ({ {| "{" %ws? inline_statements %ws? "}" |} }) -> Thunk
     eol_thunk <- ({ {| ":" %ws? noeol_statements eol |} }) -> Thunk
     indented_thunk <- ({ {| (":" / "{..}") indent
                 statements (nodent statements)*
@@ -82,7 +82,7 @@ local nomsu = [=[    file <- ({{| shebang?
     indented_nomsu <- ({("\" expression) }) -> Nomsu
 
     inline_expression <- number / variable / inline_string / inline_list / inline_nomsu
-        / inline_thunk / ("(" inline_statement ")")
+        / inline_thunk / ("(" %ws? inline_statement %ws? ")")
     noeol_expression <- indented_string / indented_nomsu / indented_list / indented_thunk
         / ("(..)" indent
             statement
@@ -101,7 +101,7 @@ local nomsu = [=[    file <- ({{| shebang?
             (expression (dotdot / tok_gap))* word ((dotdot / tok_gap) (expression / word))*
         |} }) -> FunctionCall
 
-    word <- ({ !number {%wordchar (!"'" %wordchar)*} }) -> Word
+    word <- ({ { %wordbreaker / (!number %wordchar+) } }) -> Word
     
     inline_string <- ({ '"' {|
         ({~ (("\\" -> "\") / ('\"' -> '"') / ("\n" -> "
@@ -119,7 +119,7 @@ local nomsu = [=[    file <- ({{| shebang?
 
     -- Variables can be nameless (i.e. just %) and can't contain apostrophes
     -- which is a hack to allow %foo's to parse as "%foo" and "'s" separately
-    variable <- ({ ("%" { (!"'" %wordchar)* }) }) -> Var
+    variable <- ({ ("%" { (%wordbreaker / (%wordchar+))? }) }) -> Var
 
     inline_list <- ({ {|
          ("[" %ws? ((inline_list_item comma)* inline_list_item comma?)? %ws? "]")
@@ -140,22 +140,24 @@ local nomsu = [=[    file <- ({{| shebang?
     indent <- eol (%nl ignored_line)* %nl %indented ((block_comment/line_comment) (%nl ignored_line)* nodent)?
     nodent <- eol (%nl ignored_line)* %nl %nodented
     dedent <- eol (%nl ignored_line)* (((!.) &%dedented) / (&(%nl %dedented)))
-    tok_gap <- %ws / %prev_edge / &("[" / "\" / [.,:;{("#%'])
+    tok_gap <- %ws / %prev_edge / &("[" / "\" / [.,:;{("#%] / &%wordbreaker)
     comma <- %ws? "," %ws?
     semicolon <- %ws? ";" %ws?
     dotdot <- nodent ".." %ws?
 ]=]
 local CURRENT_FILE = nil
 local whitespace = S(" \t") ^ 1
+local wordbreaker = ("'~`!@$^&*-+=|<>?/")
 local defs = {
   ws = whitespace,
   nl = P("\n"),
   tonumber = tonumber,
-  wordchar = P(1) - S(' \t\n\r%#:;,.{}[]()"\\'),
+  wordbreaker = S(wordbreaker),
+  wordchar = P(1) - S(' \t\n\r%#:;,.{}[]()"\\' .. wordbreaker),
   indented = Cmt(S(" \t") ^ 0 * (#(P(1) - S(" \t\n") + (-P(1)))), check_indent),
   nodented = Cmt(S(" \t") ^ 0 * (#(P(1) - S(" \t\n") + (-P(1)))), check_nodent),
   dedented = Cmt(S(" \t") ^ 0 * (#(P(1) - S(" \t\n") + (-P(1)))), check_dedent),
-  prev_edge = B(S(" \t\n.,:;}])\"\\")),
+  prev_edge = B(S(" \t\n.,:;}])\"\\" .. wordbreaker)),
   line_no = function(src, pos)
     local line_no = 1
     for _ in src:sub(1, pos):gmatch("\n") do
@@ -704,7 +706,7 @@ end)]]):format(concat(lua_bits, "\n"))
         self:error("Nothing to get stub from")
       end
       if type(x) == 'string' then
-        local stub = x:gsub("'", " '"):gsub("%%%S+", "%%"):gsub("%s+", " ")
+        local stub = x:gsub("([" .. tostring(wordbreaker) .. "])", " %1 "):gsub("%%%S+", "%%"):gsub("%s+", " "):gsub("%s*$", "")
         local arg_names
         do
           local _accum_0 = { }
diff --git a/nomsu.moon b/nomsu.moon
index cd80510..82867ed 100755
--- a/nomsu.moon
+++ b/nomsu.moon
@@ -71,7 +71,7 @@ nomsu = [=[
     noeol_statement <- noeol_functioncall / noeol_expression
     inline_statement <- inline_functioncall / inline_expression
 
-    inline_thunk <- ({ {| "{" inline_statements "}" |} }) -> Thunk
+    inline_thunk <- ({ {| "{" %ws? inline_statements %ws? "}" |} }) -> Thunk
     eol_thunk <- ({ {| ":" %ws? noeol_statements eol |} }) -> Thunk
     indented_thunk <- ({ {| (":" / "{..}") indent
                 statements (nodent statements)*
@@ -83,7 +83,7 @@ nomsu = [=[
     indented_nomsu <- ({("\" expression) }) -> Nomsu
 
     inline_expression <- number / variable / inline_string / inline_list / inline_nomsu
-        / inline_thunk / ("(" inline_statement ")")
+        / inline_thunk / ("(" %ws? inline_statement %ws? ")")
     noeol_expression <- indented_string / indented_nomsu / indented_list / indented_thunk
         / ("(..)" indent
             statement
@@ -102,7 +102,7 @@ nomsu = [=[
             (expression (dotdot / tok_gap))* word ((dotdot / tok_gap) (expression / word))*
         |} }) -> FunctionCall
 
-    word <- ({ !number {%wordchar (!"'" %wordchar)*} }) -> Word
+    word <- ({ { %wordbreaker / (!number %wordchar+) } }) -> Word
     
     inline_string <- ({ '"' {|
         ({~ (("\\" -> "\") / ('\"' -> '"') / ("\n" -> "
@@ -120,7 +120,7 @@ nomsu = [=[
 
     -- Variables can be nameless (i.e. just %) and can't contain apostrophes
     -- which is a hack to allow %foo's to parse as "%foo" and "'s" separately
-    variable <- ({ ("%" { (!"'" %wordchar)* }) }) -> Var
+    variable <- ({ ("%" { (%wordbreaker / (%wordchar+))? }) }) -> Var
 
     inline_list <- ({ {|
          ("[" %ws? ((inline_list_item comma)* inline_list_item comma?)? %ws? "]")
@@ -141,7 +141,7 @@ nomsu = [=[
     indent <- eol (%nl ignored_line)* %nl %indented ((block_comment/line_comment) (%nl ignored_line)* nodent)?
     nodent <- eol (%nl ignored_line)* %nl %nodented
     dedent <- eol (%nl ignored_line)* (((!.) &%dedented) / (&(%nl %dedented)))
-    tok_gap <- %ws / %prev_edge / &("[" / "\" / [.,:;{("#%'])
+    tok_gap <- %ws / %prev_edge / &("[" / "\" / [.,:;{("#%] / &%wordbreaker)
     comma <- %ws? "," %ws?
     semicolon <- %ws? ";" %ws?
     dotdot <- nodent ".." %ws?
@@ -149,13 +149,14 @@ nomsu = [=[
 
 CURRENT_FILE = nil
 whitespace = S(" \t")^1
+wordbreaker = ("'~`!@$^&*-+=|<>?/")
 defs =
-    ws:whitespace, nl: P("\n"), :tonumber
-    wordchar: P(1)-S(' \t\n\r%#:;,.{}[]()"\\')
+    ws:whitespace, nl: P("\n"), :tonumber, wordbreaker:S(wordbreaker)
+    wordchar: P(1)-S(' \t\n\r%#:;,.{}[]()"\\'..wordbreaker)
     indented: Cmt(S(" \t")^0 * (#(P(1)-S(" \t\n") + (-P(1)))), check_indent)
     nodented: Cmt(S(" \t")^0 * (#(P(1)-S(" \t\n") + (-P(1)))), check_nodent)
     dedented: Cmt(S(" \t")^0 * (#(P(1)-S(" \t\n") + (-P(1)))), check_dedent)
-    prev_edge: B(S(" \t\n.,:;}])\"\\"))
+    prev_edge: B(S(" \t\n.,:;}])\"\\"..wordbreaker))
     line_no: (src, pos)->
         line_no = 1
         for _ in src\sub(1,pos)\gmatch("\n") do line_no += 1
@@ -541,7 +542,7 @@ end)]])\format(concat(lua_bits, "\n"))
         -- Returns a single stub ("say %"), and list of arg names ({"msg"}) from a single rule def
         --   (e.g. "say %msg") or function call (e.g. FunctionCall({Word("say"), Var("msg")))
         if type(x) == 'string'
-            stub = x\gsub("'"," '")\gsub("%%%S+","%%")\gsub("%s+"," ")
+            stub = x\gsub("([#{wordbreaker}])"," %1 ")\gsub("%%%S+","%%")\gsub("%s+"," ")\gsub("%s*$","")
             arg_names = [arg for arg in x\gmatch("%%([^%s']*)")]
             return stub, arg_names
         switch x.type