diff options
| author | Bruce Hill <bitbucket@bruce-hill.com> | 2017-09-12 21:48:35 -0700 |
|---|---|---|
| committer | Bruce Hill <bitbucket@bruce-hill.com> | 2017-09-12 21:48:35 -0700 |
| commit | 7d6af57f2c813d65f5148972ad737a82edd68f19 (patch) | |
| tree | aac0391c8190f012e89375424b24d718135a972e /examples | |
| parent | 0615d127b5fb8aa4b031cb9754b29654e432f641 (diff) | |
Added readme and moved examples into a directory.
Diffstat (limited to 'examples')
| -rw-r--r-- | examples/parser_tests.nom | 197 | ||||
| -rwxr-xr-x | examples/sample_code.nom | 134 | ||||
| -rw-r--r-- | examples/sample_game.nom | 194 | ||||
| -rw-r--r-- | examples/tutorial.nom | 211 |
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" + |
