aboutsummaryrefslogtreecommitdiff
path: root/examples
diff options
context:
space:
mode:
authorBruce Hill <bitbucket@bruce-hill.com>2017-09-12 21:48:35 -0700
committerBruce Hill <bitbucket@bruce-hill.com>2017-09-12 21:48:35 -0700
commit7d6af57f2c813d65f5148972ad737a82edd68f19 (patch)
treeaac0391c8190f012e89375424b24d718135a972e /examples
parent0615d127b5fb8aa4b031cb9754b29654e432f641 (diff)
Added readme and moved examples into a directory.
Diffstat (limited to 'examples')
-rw-r--r--examples/parser_tests.nom197
-rwxr-xr-xexamples/sample_code.nom134
-rw-r--r--examples/sample_game.nom194
-rw-r--r--examples/tutorial.nom211
4 files changed, 736 insertions, 0 deletions
diff --git a/examples/parser_tests.nom b/examples/parser_tests.nom
new file mode 100644
index 0000000..4f8af85
--- /dev/null
+++ b/examples/parser_tests.nom
@@ -0,0 +1,197 @@
+run file "core.nom"
+
+test: say "foo" ..yields ".."
+ |Call [say %]:
+ | "foo"
+
+test: say (4) ..yields ".."
+ |Call [say %]:
+ | 4
+
+test:
+ rule "fart": say "poot"
+..yields ".."
+ |Call [rule % %]:
+ | "fart"
+ | Thunk:
+ | Call [say %]:
+ | "poot"
+
+test:
+ rule "doublefart":
+ say "poot"
+ say "poot"
+..yields ".."
+ |Call [rule % %]:
+ | "doublefart"
+ | Thunk:
+ | Call [say %]:
+ | "poot"
+ | Call [say %]:
+ | "poot"
+
+test: say (subexpressions work) ..yields ".."
+ |Call [say %]:
+ | Call [subexpressions work]!
+
+test: say ["lists", "work"] ..yields ".."
+ |Call [say %]:
+ | List:
+ | "lists"
+ | "work"
+
+test: say [] ..yields ".."
+ |Call [say %]:
+ | <Empty List>
+
+test:
+ say [..]
+ 1, 2
+ 3
+..yields ".."
+ |Call [say %]:
+ | List:
+ | 1
+ | 2
+ | 3
+
+test:
+ say both [..]
+ 1,2
+ ..and [..]
+ 3,4
+..yields ".."
+ |Call [say both % and %]:
+ | List:
+ | 1
+ | 2
+ | List:
+ | 3
+ | 4
+
+test:
+ say both..
+ "hello"
+ and "world"
+..yields ".."
+ |Call [say both % and %]:
+ | "hello"
+ | "world"
+
+test:
+ say both ..
+ "a list:"
+ and [..]
+ 1,2,(three),(4)
+..yields ".."
+ |Call [say both % and %]:
+ | "a list:"
+ | List:
+ | 1
+ | 2
+ | Call [three]!
+ | 4
+
+test:
+ if 1: yes
+ ..else: no
+..yields ".."
+ |Call [if % % else %]:
+ | 1
+ | Thunk:
+ | Call [yes]!
+ | Thunk:
+ | Call [no]!
+
+test:
+ if 1: yes ..else: no
+..yields ".."
+ |Call [if % % else %]:
+ | 1
+ | Thunk:
+ | Call [yes]!
+ | Thunk:
+ | Call [no]!
+
+test: say (do: return 5)
+..yields ".."
+ |Call [say %]:
+ | Call [do %]:
+ | Thunk:
+ | Call [return %]:
+ | 5
+
+test:
+ say (..)
+ fn call
+..yields ".."
+ |Call [say %]:
+ | Call [fn call]!
+
+test:
+ do: say "one liner"
+ ..also: say "another one liner"
+..yields ".."
+ |Call [do % also %]:
+ | Thunk:
+ | Call [say %]:
+ | "one liner"
+ | Thunk:
+ | Call [say %]:
+ | "another one liner"
+
+test:
+ say (..)
+ do:
+ say "hi"
+ return 5
+ say "bye"
+..yields ".."
+ |Call [say %]:
+ | Call [do %]:
+ | Thunk:
+ | Call [say %]:
+ | "hi"
+ | Call [return %]:
+ | 5
+ | Call [say %]:
+ | "bye"
+
+
+test: say (1 + (-(2 * 3)))
+..yields ".."
+ |Call [say %]:
+ | Call [% + %]:
+ | 1
+ | Call [- %]:
+ | Call [% * %]:
+ | 2
+ | 3
+
+test:
+ if %x:
+ x
+ ..else: if %y:
+ y
+ ..else:
+ z
+..yields ".."
+ |Call [if % % else %]:
+ | Var[x]
+ | Thunk:
+ | Call [x]!
+ | Thunk:
+ | Call [if % % else %]:
+ | Var[y]
+ | Thunk:
+ | Call [y]!
+ | Thunk:
+ | Call [z]!
+
+
+test:
+ don't fuck this up
+..yields ".."
+ |Call [don't fuck this up]!
+
+say "All tests passed!"
diff --git a/examples/sample_code.nom b/examples/sample_code.nom
new file mode 100755
index 0000000..c8b48f7
--- /dev/null
+++ b/examples/sample_code.nom
@@ -0,0 +1,134 @@
+(# This is just a comment #)
+(# Nested comments (# like this #) work fine #)
+run file "core.nom"
+
+say "foo"
+
+say (4)
+
+(# "rule" is just a function that takes a function call spec and a block of code to run,
+ and stores the function definition #)
+rule "fart": say "poot"
+
+fart
+
+(# multi-line strings: #)
+say ".."
+ | Once upon a time
+ |there was a very
+
+
+ |long string
+ |
+ | with
+ | rather
+ | silly
+ | indentation
+
+ | and "quotes"
+ |.." (even fakeouts like that) "
+ |(done)
+ |
+
+rule "doublefart": (# this farts twice #)
+ say "poot"
+ say "poot"
+
+doublefart
+
+rule "subex work": return "subexpressions work"
+
+say (subex work)
+
+say (..)
+ subex work
+
+say ["lists", "work"]
+
+say []
+
+say [..]
+ 1, 2
+ 3
+
+rule "say both %one and %two":
+ say %one
+ say %two
+
+say both [..]
+ 1,2
+..and [..]
+ 3,4
+
+
+say both..
+ "hello"
+ and "world"
+
+rule "three": return 3
+say both ..
+ "a list:"
+ and [..]
+ 1,2,(three),(4)
+
+if 1: yes
+..else: no
+
+if 1: yes ..else: no
+
+say (do: return 5)
+
+rule "do %one also %two":
+ do %one
+ do %two
+
+do: say "one liner"
+..also: say "another one liner"
+
+say (do: return "wow")
+
+say (1 + (-(2 * 3)))
+
+say (2 + (..)
+ 3 * 4
+..)
+
+if %x:
+ say "one"
+..else: if %y:
+ say "two"
+..else:
+ say "three"
+
+
+printf [..]
+ ".."
+ |this is a longstring
+ |
+ .., "with", ".."
+ | multiple lines
+
+rule "%n bottles":
+ lua block [..]
+ ".."
+ |do
+ | print("running raw lua code...")
+ | local n =
+ .., %n, ".."
+ |
+ | for i=n,1,-1 do
+ | print(tostring(i).." bottles of beer on the wall. Take one down, pass it around,")
+ | end
+ | print("no more bottles of beer on the wall.")
+ |end
+ nil
+9 bottles
+
+rule "dumsum %nums":
+ let "sum" = 0
+ for "n" in %nums:
+ let "sum" = (%sum + %n)
+ return %sum
+
+say (dumsum [1,2,3])
+
diff --git a/examples/sample_game.nom b/examples/sample_game.nom
new file mode 100644
index 0000000..010e610
--- /dev/null
+++ b/examples/sample_game.nom
@@ -0,0 +1,194 @@
+run file "core.nom"
+lua block ".."
+ |do -- Use a closure to hide this behind the accessor rules
+ | local relations = setmetatable({}, {__index=function(self,key)
+ | local t = {}
+ | self[key] = t
+ | return t
+ | end})
+ | compiler:def("%key %relation ?", function(_,vars)
+ | return relations[vars.relation][vars.key]
+ | end)
+ | compiler:def("%key %relation = %value", function(_,vars)
+ | relations[vars.relation][vars.key] = vars.value
+ | end)
+ | compiler:def("* %relation = %value", function(_,vars)
+ | local result = {}
+ | for k,v in pairs(relations[vars.relation]) do
+ | if utils.equivalent(v, vars.value) then
+ | table.insert(result, k)
+ | end
+ | end
+ | return result
+ | end)
+ |end
+
+rule "set all * %relation = %value":
+ for "key" in (* %relation = %value):
+ %key %relation = %value
+
+rule "you":
+ lua expr "(you or 'Anonymous')"
+
+rule ["make %person %action", "make %person do %action"]:
+ lua block [..]
+ "do"
+ "\n local old_you = you"
+ "\n you = vars.person"
+ "\n ret = compiler:call('do %', vars.action)"
+ "\n you = old_you"
+ "\nend"
+
+say "===================================================="
+say " NEW GAME"
+say "===================================================="
+rule "everyone approves %action": yes
+
+rule "with everyone's approval %action":
+ if (everyone approves %action):
+ do %action
+ ..else:
+ say "You do not have the will of the people! >:("
+
+restrict "rule % %" to within "with everyone's approval %"
+restrict "make % %" to within "with everyone's approval %"
+restrict "set all * % = %" to within "with everyone's approval %"
+restrict "% % = %" to within "with everyone's approval %"
+restrict "restrict % to within %" to within "with everyone's approval %"
+
+with everyone's approval:
+ rule "propose %action":
+ if ("pending proposal" "is" ?):
+ say "Sorry, an action is already pending."
+ ..else:
+ say "Proposing..."
+ "pending proposal" "is" = %action
+
+ rule "unpropose":
+ let "pending" = ("pending proposal" "is" ?)
+ set all * %pending = (nil)
+ "pending proposal" "is" = (nil)
+
+
+ rule "mark %who as approving %action":
+ %who %action = "approved"
+
+ rule "mark %who as rejecting %action":
+ %who %action = "rejected"
+
+ rule ["approve", "vote yes", "vote yea"]:
+ let "pending" = ("pending proposal" "is" ?)
+ if (%pending == (nil)):
+ say "Nothing is currently pending!"
+ return
+ mark (you) as approving %pending
+ if (everyone approves %pending):
+ say "The motion passes!"
+ with everyone's approval %pending
+ unpropose
+ ..else:
+ let "approvers" = (number of (* %pending = "approved"))
+ let "num-players" = (number of (players))
+ printf [%approvers, "/", %num-players, " players have approved"]
+
+ rule ["reject", "vote no", "vote nay", "veto", "disapprove"]:
+ let "pending" = ("pending proposal" "is" ?)
+ if (%pending == (nil)):
+ say "Nothing is currently pending!"
+ return
+ mark (you) as rejecting %pending
+ unpropose
+
+ rule ["players", "everyone", "everybody", "all players"]:
+ * "is a player" = (yes)
+
+ rule "join":
+ (you) "is a player" = (yes)
+ printf ["Welcome to the game, ", you,"!"]
+
+ allow "unpropose" to use "set all * % = %"
+ allow ["join", "mark % as approving %", "mark % as rejecting %", "propose %", "unpropose"] to use "% % = %"
+ restrict "unpropose" to within ["approve", "reject"]
+ restrict "mark % as approving %" to within ["propose %", "approve"]
+ restrict "mark % as rejecting %" to within ["propose %", "reject"]
+
+ rule "everyone approves %action":
+ (number of (players)) == (number of (* %action = "approved"))
+
+join
+
+propose:
+ say "fart"
+approve
+
+propose:
+ "democracy" "is possible" = (yes)
+ if ("democracy" "is possible" ?):
+ say "DEMOCRACY WORKS!!!"
+approve
+
+propose:
+ rule "fart":
+ say "poot"
+ say "fart should have been defined"
+approve
+say "doop"
+fart
+
+propose:
+ rule "open election %candidates":
+ if ("candidates" "are" ?):
+ say "An election is already in progress."
+ ..else:
+ "candidates" "are" = %candidates
+
+ rule "close the election":
+ set all * "votes for" = (nil)
+ "candidates" "are" = (nil)
+
+ rule "vote for %candidate":
+ (you) "votes for" = %candidate
+ let "vote-percent" = ((number of (* "votes for" = %candidate)) / (number of (players)))
+ printf ["Vote cast. ",%candidate," now has ",(100 * %vote-percent),"% of the votes."]
+ if (%vote-percent > 0.5):
+ printf ["The winner of the election is:", %candidate]
+ close the election
+
+ allow ["open election %", "close the election", "vote for %"] to use ["% % = %"]
+ allow ["close the election"] to use ["set all * % = %"]
+approve
+
+propose:
+ rule "as bill %action":
+ if ((you) == "Anonymous"):
+ make "bill" do %action
+ ..else:
+ printf ["Who do you think you are, ", you,"?"]
+ allow ["as bill %"] to use ["make % %"]
+approve
+as bill: join
+
+propose:
+ rule "as dave %action":
+ if ((you) == "Anonymous"):
+ make "dave" do %action
+ ..else:
+ printf ["Who do you think you are, ", you,"?"]
+ allow ["as dave %"] to use ["make % %"]
+approve
+as bill: approve
+as dave: join
+
+open election ["tom", "dick", "harry"]
+vote for "dick"
+as bill: vote for "dick"
+
+propose:
+ rule "take a shit": say "shit taken."
+
+approve
+as bill: approve
+as dave: approve
+take a shit
+say "Done."
+
diff --git a/examples/tutorial.nom b/examples/tutorial.nom
new file mode 100644
index 0000000..1b25f69
--- /dev/null
+++ b/examples/tutorial.nom
@@ -0,0 +1,211 @@
+(# Comments use (# ... #), and can be nested #)
+
+(# Import files like so: #)
+run file "core.nom"
+
+(# Numbers: #)
+23
+4.5
+(# Since this language cross-compiles to lua, integers and floating point numbers are
+ both represented using the same primitive. #)
+
+(# Strings: #)
+"asdf"
+".."
+ |This is a multi-line string
+ |that starts with ".." and includes each indented line that starts with a "|"
+ |until the indentation ends
+
+(# Lists: #)
+[1,2,3]
+[..]
+ "like multi-line strings, lists have an indented form", "that can use commas too"
+ "or just newlines to separate items"
+ 5
+ 6,7,8
+
+(# Function calls: #)
+say "Hello world!"
+
+(# Function definition: #)
+rule "say both %first and also %second":
+ (# Variables use the "%" sign: #)
+ say %first
+ say %second
+
+(# Function calls can have parts of the function's name spread throughout.
+ Everything that's not a literal value is treated as part of the function's name #)
+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
+ say "-- she said"
+"Howdy pardner" is what she said
+
+(# The language only reserves []{}().,:;% as special characters, so functions and variables
+ can have really funky names! #)
+rule ">> %foo-bar###^ --> %@@& _~-^-~_~-^ %1 !":
+ say %foo-bar###^
+ say %@@&
+ say %1
+>> "wow" --> "so flexible!" _~-^-~_~-^ "even numbers can be variables!" !
+(# Though literals can't be used in function names #)
+
+(# Math and logic operations are just treated the same as function calls in the syntax #)
+say (2 + 3)
+(# So it's easy to define your own operators #)
+rule "%a ++ %b": 2 * (%a + %b)
+say (2 ++ 3)
+
+
+(# Code blocks start with ":" and either continue until the end of the line
+ or are indented blocks #)
+
+(# One liner: #)
+: say "hi"
+
+(# Block version: #)
+:
+ say "one"
+ say "two"
+
+(# So the function definitions above are actually just passing a regular string, like
+ "say both %first and also %second", and a code block to a function called "rule % %"
+ that takes two arguments. #)
+
+(# Line continuations work by either ending a line with ".." and continuing with an indented block: #)
+say..
+ both "Tom" and
+ also
+ "Sawyer"
+
+(# Or by starting the next line with ".." #)
+say both "Bruce"
+..and also "Lee"
+
+(# This can be mixed and matched: #)
+say both..
+ "Rick"
+..and also..
+ "Moranis"
+
+(# And combined with the block forms of literals: #)
+say both ".."
+ |Four score and seven years ago our fathers brought forth, upon this continent,
+ |a new nation, conceived in liberty, and dedicated to the proposition that
+ |"all men are created equal"
+..and also..
+ "-- Abraham Lincoln"
+
+rule "my favorite number": return 23
+(# Subexpressions are wrapped in parentheses: #)
+(# printf takes a list of bits that are converted to strings and concatenated together, and printed #)
+printf ["My favorite number is ", my favorite number]
+(# There's a multi-line indented block form for subexpressions too: #)
+printf [..]
+ "My favorite number is still ", (..)
+ my favorite
+ number
+
+(# There's a few macros in the language for things like conditional branches and logic/math
+ operations, but they can be thought of as basically the same as functions.
+ There are no keywords in the language! #)
+if (1 < 10):
+ say "One is less than ten"
+..else:
+ say "One is not less than ten"
+
+(# Breakdown of the above: #)
+(# Function call (actually a macro) to "if % % else %" #)
+(# First argument is a subexpression that is a function call (also a macro) to "% < %"
+ that performs a comparison on its arguments, 1 and 10 #)
+(# Second argument is a block of code that includes a function call to "say %", the "if" body #)
+(# Third argument is a block of code that includes a different function call to "say %", the "else" body #)
+
+(# Line continuations can be used for "elseif" #)
+if (1 > 10):
+ say "First condition"
+..else: if (1 > 5):
+ say "Second condition"
+..else:
+ say "Last condition"
+
+(# ^that's the same as: #)
+if (1 > 10):
+ say "First condition"
+..else:
+ if (1 > 5):
+ say "Second condition"
+ ..else:
+ say "Last condition"
+
+(# Variables are modified with a macro, "let % = %" #)
+let "numbers" = [5,6,7]
+
+(# Looping: #)
+printf ["Looping over: ",%numbers,"!"]
+for "number" in %numbers:
+ say (%number + 100)
+
+rule "sing %starting-bottles bottles of beer":
+ for "n" in (%starting-bottles down through 0):
+ printf [..]
+ (%n if (%n > 0) else "No more")
+ (" bottle" if (%n == 1) else " bottles")
+ " of beer on the wall."
+ ("" if (%n == 0) else " Take one down, pass it around...")
+
+sing 9 bottles of beer
+
+
+(# Note that because math and logic operations are just macros, they require a lot
+ of parentheses to disambiguate. There's no PEMDAS. #)
+say (5 + (4 * (- (1 + (6 + 2)))))
+(# For convenience, +,*,"and", and "or" have been hand defined to work with up to 4 operands: #)
+1 + 2 + 3 + 4
+1 * 2 * 3 * 4
+1 and 2 and 3 and 4
+1 or 2 or 3 or 4
+(# Longer lists can use "sum of %", "product of %", "all of %", and "any of %", respectively, or lots of parentheses. #)
+sum of [1,2,3,4,5,6,7]
+product of [1,2,3,4,5,6,7]
+all of [1,1,1,1,0,1,1]
+any of [0,0,0,0,1,0,0]
+(# And 3-operand chained inequality comparisons have been defined: #)
+1 < 2 <= 3
+
+
+(# Macros: #)
+(# The "lua block %" and "lua expr %" macros can be used to write raw lua code: #)
+rule "say time":
+ lua block ".."
+ |io.write("The OS time is: ")
+ |io.write(tostring(os.time()).."\n")
+say time
+printf ["Math expression: ", lua expr "(1 + 2*3 + 3*4)^2"]
+(# In the lua environment, "vars" can be used to get local variables/function args, and "game"
+ can be used to access the compiler, function defs, and other things #)
+rule "square root of %n":
+ return (lua expr "math.sqrt(vars.n)")
+printf ["the square root of 2 is ", square root of 2]
+
+(# Macros can be defined as functions that take unprocessed syntax trees and return lua code #)
+(# "macro block %" is for defining macros that produce blocks of code, not values #)
+macro block "unless %condition %body":
+ concat [..]
+ (# "% as lua expr" and "% as lua block" are two useful helper functions here. #)
+ "if not (", %condition as lua expr, ") then"
+ (# Extract the inner part of the code block's body and insert it: #)
+ "\n ", (lua expr "vars.body.value.value") as lua block
+ "\nend"
+
+unless (1 > 10):
+ say "Macros work!"
+ say "It looks like a keyword, but there's no magic here!"
+
+(# and "macro %" is for defining macros that produce an expression #)
+macro "%value as a boolean":
+ concat ["(not not (", %value as lua expr, "))"]
+macro "yep": "true"
+