Changed comments to use # and #.. instead of (# #), which is more

consistent with the language's attitude towards code blocks and
indentation. No more long-range action closing parens.
This commit is contained in:
Bruce Hill 2017-09-14 00:09:54 -07:00
parent 6b5fbd436b
commit 0f228d2d25
6 changed files with 136 additions and 114 deletions

View File

@ -1,4 +1,3 @@
(# Global import #)
lua block ".." lua block ".."
|utils = require('utils') |utils = require('utils')
@ -107,12 +106,11 @@ macro "%a or %b or %c or %d":
macro "%a mod %b": concat ["(",%a as lua expr," mod ",%b as lua expr,")"] macro "%a mod %b": concat ["(",%a as lua expr," mod ",%b as lua expr,")"]
macro "- %a": concat ["-(",%a as lua expr,")"] macro "- %a": concat ["-(",%a as lua expr,")"]
macro "not %a": concat ["not (",%a as lua expr,")"] macro "not %a": concat ["not (",%a as lua expr,")"]
macro "# %a": concat ["#(",%a as lua expr,")"]
(# This does equivalence checking instead of identity checking. #)
rule "%a == %b": rule "%a == %b":
lua expr "utils.equivalent(vars.a, vars.b)" lua expr "((vars.a == vars.b) or utils.equivalent(vars.a, vars.b))"
macro "%a != %b": concat ["not (",%a as lua expr," == ",%b as lua expr,")"] rule "%a != %b":
lua expr "((vars.a ~= vars.b) or not utils.equivalent(vars.a, vars.b))"
rule "say %str": rule "say %str":
lua block ["print(utils.repr(", %str, "))"] lua block ["print(utils.repr(", %str, "))"]

15
examples/sample_code.nom Executable file → Normal file
View File

@ -1,18 +1,21 @@
(# This is just a comment #) # This is just a comment
(# Nested comments (# like this #) work fine #) #.. Block comments
start with a #.. and
continue until dedent
run file "core.nom" run file "core.nom"
say "foo" say "foo"
say (4) say (4)
(# "rule" is just a function that takes a function call spec and a block of code to run, #.. "rule" is just a function that takes a function call spec and a block of code to run,
and stores the function definition #) and stores the function definition
rule "fart": say "poot" rule "fart": say "poot"
fart fart
(# multi-line strings: #) # multi-line strings:
say ".." say ".."
| Once upon a time | Once upon a time
|there was a very |there was a very
@ -30,7 +33,7 @@ say ".."
|(done) |(done)
| |
rule "doublefart": (# this farts twice #) rule "doublefart": # this farts twice
say "poot" say "poot"
say "poot" say "poot"

View File

@ -31,13 +31,13 @@ rule "you":
lua expr "(you or 'Anonymous')" lua expr "(you or 'Anonymous')"
rule ["make %person %action", "make %person do %action"]: rule ["make %person %action", "make %person do %action"]:
lua block [..] lua block ".."
"do" |do
"\n local old_you = you" | local old_you = you
"\n you = vars.person" | you = vars.person
"\n ret = compiler:call('do %', vars.action)" | ret = compiler:call('do %', vars.action)
"\n you = old_you" | you = old_you
"\nend" |end
say "====================================================" say "===================================================="
say " NEW GAME" say " NEW GAME"

View File

@ -1,22 +1,24 @@
(# Comments use (# ... #), and can be nested #) # One liner comments start with # and go till end of line
#.. Multi-line comments start with #.. and
continue until dedent
(# Import files like so: #) # Import files like so:
run file "core.nom" run file "core.nom"
(# Numbers: #) # Numbers:
23 23
4.5 4.5
(# Since this language cross-compiles to lua, integers and floating point numbers are #.. Since this language cross-compiles to lua, integers and floating point numbers are
both represented using the same primitive. #) both represented using the same primitive.
(# Strings: #) # Strings:
"asdf" "asdf"
".." ".."
|This is a multi-line string |This is a multi-line string with a #.. fake comment
|that starts with ".." and includes each indented line that starts with a "|" |that starts with ".." and includes each indented line that starts with a "|"
|until the indentation ends |until the indentation ends
(# Lists: #) # Lists:
[1,2,3] [1,2,3]
[..] [..]
"like multi-line strings, lists have an indented form", "that can use commas too" "like multi-line strings, lists have an indented form", "that can use commas too"
@ -24,73 +26,73 @@ run file "core.nom"
5 5
6,7,8 6,7,8
(# Function calls: #) # Function calls:
say "Hello world!" say "Hello world!"
(# Function definition: #) # Function definition:
rule "say both %first and also %second": rule "say both %first and also %second":
(# Variables use the "%" sign: #) # Variables use the "%" sign:
say %first say %first
say %second say %second
(# Function calls can have parts of the function's name spread throughout. #.. 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 #) Everything that's not a literal value is treated as part of the function's name
say both "Hello" and also "again!" say both "Hello" and also "again!"
(# Functions can even have their name at the end: #) # Functions can even have their name at the end:
rule "%what-she-said is what she said": rule "%what-she-said is what she said":
say %what-she-said say %what-she-said
say "-- she said" say "-- she said"
"Howdy pardner" is what 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 and variables
can have really funky names! #) can have really funky names!
rule ">> %foo-bar###^ --> %@@& _~-^-~_~-^ %1 !": rule ">> %foo-bar$$$^ --> %@@& _~-^-~_~-^ %1 !":
say %foo-bar###^ say %foo-bar$$$^
say %@@& say %@@&
say %1 say %1
>> "wow" --> "so flexible!" _~-^-~_~-^ "even numbers can be variables!" ! >> "wow" --> "so flexible!" _~-^-~_~-^ "even numbers can be variables!" !
(# Though literals can't be used in function names #) # Though literals can't be used in function names
(# Math and logic operations are just treated the same as function calls in the syntax #) # Math and logic operations are just treated the same as function calls in the syntax
say (2 + 3) say (2 + 3)
(# So it's easy to define your own operators #) # So it's easy to define your own operators
rule "%a ++ %b": 2 * (%a + %b) rule "%a ++ %b": 2 * (%a + %b)
say (2 ++ 3) say (2 ++ 3)
(# Code blocks start with ":" and either continue until the end of the line #.. Code blocks start with ":" and either continue until the end of the line
or are indented blocks #) or are indented blocks
(# One liner: #) # One liner:
: say "hi" : say "hi"
(# Block version: #) # Block version:
: :
say "one" say "one"
say "two" say "two"
(# So the function definitions above are actually just passing a regular string, like #.. 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 % %" "say both %first and also %second", and a code block to a function called "rule % %"
that takes two arguments. #) that takes two arguments.
(# Line continuations work by either ending a line with ".." and continuing with an indented block: #) # Line continuations work by either ending a line with ".." and continuing with an indented block:
say.. say..
both "Tom" and both "Tom" and
also also
"Sawyer" "Sawyer"
(# Or by starting the next line with ".." #) # Or by starting the next line with ".."
say both "Bruce" say both "Bruce"
..and also "Lee" ..and also "Lee"
(# This can be mixed and matched: #) # This can be mixed and matched:
say both.. say both..
"Rick" "Rick"
..and also.. ..and also..
"Moranis" "Moranis"
(# And combined with the block forms of literals: #) # And combined with the block forms of literals:
say both ".." say both ".."
|Four score and seven years ago our fathers brought forth, upon this continent, |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 |a new nation, conceived in liberty, and dedicated to the proposition that
@ -99,31 +101,31 @@ say both ".."
"-- Abraham Lincoln" "-- Abraham Lincoln"
rule "my favorite number": return 23 rule "my favorite number": return 23
(# Subexpressions are wrapped in parentheses: #) # Subexpressions are wrapped in parentheses:
(# printf takes a list of bits that are converted to strings and concatenated together, and printed #) # printf takes a list of bits that are converted to strings and concatenated together, and printed
printf ["My favorite number is ", my favorite number] printf ["My favorite number is ", my favorite number]
(# There's a multi-line indented block form for subexpressions too: #) # There's a multi-line indented block form for subexpressions too:
printf [..] printf [..]
"My favorite number is still ", (..) "My favorite number is still ", (..)
my favorite my favorite
number number
(# There's a few macros in the language for things like conditional branches and logic/math #.. 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. operations, but they can be thought of as basically the same as functions.
There are no keywords in the language! #) There are no keywords in the language!
if (1 < 10): if (1 < 10):
say "One is less than ten" say "One is less than ten"
..else: ..else:
say "One is not less than ten" say "One is not less than ten"
(# Breakdown of the above: #) #.. Breakdown of the above:
(# Function call (actually a macro) to "if % % else %" #) Function call (actually a macro) to "if % % else %"
(# First argument is a subexpression that is a function call (also a macro) to "% < %" First argument is a subexpression that is a function call (also a macro) to "% < %"
that performs a comparison on its arguments, 1 and 10 #) 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 #) 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 #) 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" #) # Line continuations can be used for "elseif"
if (1 > 10): if (1 > 10):
say "First condition" say "First condition"
..else: if (1 > 5): ..else: if (1 > 5):
@ -131,7 +133,7 @@ if (1 > 10):
..else: ..else:
say "Last condition" say "Last condition"
(# ^that's the same as: #) # ^that's the same as:
if (1 > 10): if (1 > 10):
say "First condition" say "First condition"
..else: ..else:
@ -140,10 +142,10 @@ if (1 > 10):
..else: ..else:
say "Last condition" say "Last condition"
(# Variables are modified with a macro, "let % = %" #) # Variables are modified with a macro, "let % = %"
let "numbers" = [5,6,7] let "numbers" = [5,6,7]
(# Looping: #) # Looping:
printf ["Looping over: ",%numbers,"!"] printf ["Looping over: ",%numbers,"!"]
for "number" in %numbers: for "number" in %numbers:
say (%number + 100) say (%number + 100)
@ -158,45 +160,45 @@ rule "sing %starting-bottles bottles of beer":
sing 9 bottles of beer sing 9 bottles of beer
#.. Note that because math and logic operations are just macros, they require a lot
(# Note that because math and logic operations are just macros, they require a lot of parentheses to disambiguate. There's no PEMDAS.
of parentheses to disambiguate. There's no PEMDAS. #)
say (5 + (4 * (- (1 + (6 + 2))))) say (5 + (4 * (- (1 + (6 + 2)))))
(# For convenience, +,*,"and", and "or" have been hand defined to work with up to 4 operands: #) # 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 * 2 * 3 * 4 1 * 2 * 3 * 4
1 and 2 and 3 and 4 1 and 2 and 3 and 4
1 or 2 or 3 or 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. #) # 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] sum of [1,2,3,4,5,6,7]
product 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] all of [1,1,1,1,0,1,1]
any of [0,0,0,0,1,0,0] any of [0,0,0,0,1,0,0]
(# And 3-operand chained inequality comparisons have been defined: #) # And 3-operand chained inequality comparisons have been defined:
1 < 2 <= 3 1 < 2 <= 3
(# Macros: #) # Macros:
(# The "lua block %" and "lua expr %" macros can be used to write raw lua code: #) # The "lua block %" and "lua expr %" macros can be used to write raw lua code:
rule "say the time": rule "say the time":
lua block ".." lua block ".."
|io.write("The OS time is: ") |io.write("The OS time is: ")
|io.write(tostring(os.time()).."\n") |io.write(tostring(os.time()).."\n")
say the time say the time
printf ["Math expression result is: ", lua expr "(1 + 2*3 + 3*4)^2"] printf ["Math expression result is: ", lua expr "(1 + 2*3 + 3*4)^2"]
(# In the lua environment, "vars" can be used to get local variables/function args, and
"compiler" can be used to access the compiler, function defs, and other things #) #.. In the lua environment, "vars" can be used to get local variables/function args, and
"compiler" can be used to access the compiler, function defs, and other things
rule "square root of %n": rule "square root of %n":
return (lua expr "math.sqrt(vars.n)") return (lua expr "math.sqrt(vars.n)")
printf ["the square root of 2 is ", square root of 2] 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 #) # 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 %" is for defining macros that produce blocks of code, not values
macro block "unless %condition %body": macro block "unless %condition %body":
concat [..] concat [..]
(# "% as lua expr" and "% as lua block" are two useful helper functions here. #) # "% as lua expr" and "% as lua block" are two useful helper functions here.
"if not (", %condition as lua expr, ") then" "if not (", %condition as lua expr, ") then"
(# Extract the inner part of the code block's body and insert it: #) # Extract the inner part of the code block's body and insert it:
"\n ", (lua expr "vars.body.value.value") as lua block "\n ", (lua expr "vars.body.value.value") as lua block
"\nend" "\nend"
@ -204,7 +206,7 @@ unless (1 > 10):
say "Macros work!" say "Macros work!"
say "It looks like a keyword, but there's no magic here!" say "It looks like a keyword, but there's no magic here!"
(# and "macro %" is for defining macros that produce an expression #) # and "macro %" is for defining macros that produce an expression
macro "%value as a boolean": macro "%value as a boolean":
concat ["(not not (", %value as lua expr, "))"] concat ["(not not (", %value as lua expr, "))"]
macro "yep": "true" macro "yep": "true"

View File

@ -5,11 +5,6 @@ local INDENT = " "
lpeg.setmaxstack(10000) lpeg.setmaxstack(10000)
local P, V, S, Cg, C, Cp, B, Cmt local P, V, S, Cg, C, Cp, B, Cmt
P, V, S, Cg, C, Cp, B, Cmt = lpeg.P, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt P, V, S, Cg, C, Cp, B, Cmt = lpeg.P, lpeg.V, lpeg.S, lpeg.Cg, lpeg.C, lpeg.Cp, lpeg.B, lpeg.Cmt
local wordchar = P(1) - S(' \t\n\r%:;,.{}[]()"')
local comment = re.compile([[comment <- "(#" (comment / ((! "#)") .))* "#)"]])
local whitespace = (S(" \t") + comment) ^ 1
local nl = P("\n")
local blank_line = whitespace ^ -1 * nl
local get_line_indentation local get_line_indentation
get_line_indentation = function(line) get_line_indentation = function(line)
local indent_amounts = { local indent_amounts = {
@ -18,7 +13,7 @@ get_line_indentation = function(line)
} }
do do
local sum = 0 local sum = 0
local leading_space = line:gsub("([\t ]*).*", "%1") local leading_space = line:match("[\t ]*")
for c in leading_space:gmatch("[\t ]") do for c in leading_space:gmatch("[\t ]") do
sum = sum + indent_amounts[c] sum = sum + indent_amounts[c]
end end
@ -64,13 +59,31 @@ make_parser = function(lingo, extra_definitions)
end end
return end_pos return end_pos
end end
local wordchar = P(1) - S(' \t\n\r%#:;,.{}[]()"')
local nl = P("\n")
local whitespace = S(" \t") ^ 1
local blank_line = whitespace ^ -1 * nl
local line_comment = re.compile([=[ "#" [^%nl]* ]=], {
nl = nl
})
local block_comment = re.compile([=[ "#.." (!%nl .)* (%indent (!%dedent %nl [^%nl]*)*)
]=], {
nl = nl,
whitespace = whitespace,
indent = #(nl * blank_line ^ 0 * Cmt(S(" \t") ^ 0, check_indent)),
dedent = #(nl * blank_line ^ 0 * Cmt(S(" \t") ^ 0, check_dedent)),
new_line = nl * blank_line ^ 0 * Cmt(S(" \t") ^ 0, check_nodent)
})
blank_line = ((Cmt(whitespace ^ -1, check_nodent) * (block_comment + line_comment)) ^ -1 + whitespace ^ -1) * nl
local defs = { local defs = {
wordchar = wordchar, wordchar = wordchar,
nl = nl, nl = nl,
ws = whitespace, ws = whitespace,
comment = comment, blank_line = blank_line,
block_comment = block_comment,
line_comment = line_comment,
eol = #nl + (P("") - P(1)), eol = #nl + (P("") - P(1)),
word_boundary = whitespace + B(P("..")) + B(S("\";)]")) + #S("\":([") + #((whitespace + nl) ^ 0 * P("..")), word_boundary = whitespace ^ -1 + B(P("..")) + B(S("\";)]")) + #S("\":([") + #((whitespace + nl) ^ 0 * P("..")),
indent = #(nl * blank_line ^ 0 * Cmt(whitespace ^ -1, check_indent)), indent = #(nl * blank_line ^ 0 * Cmt(whitespace ^ -1, check_indent)),
dedent = #(nl * blank_line ^ 0 * Cmt(whitespace ^ -1, check_dedent)), dedent = #(nl * blank_line ^ 0 * Cmt(whitespace ^ -1, check_dedent)),
new_line = nl * blank_line ^ 0 * Cmt(whitespace ^ -1, check_nodent), new_line = nl * blank_line ^ 0 * Cmt(whitespace ^ -1, check_nodent),
@ -244,7 +257,7 @@ do
if self.debug then if self.debug then
print("PARSING:\n" .. tostring(str)) print("PARSING:\n" .. tostring(str))
end end
local lingo = [=[ file <- ({ {| %ws? %new_line? {:body: block :} %new_line? %ws? (errors)? |} }) -> File local lingo = [=[ file <- ({ {| %blank_line* {:body: block :} %blank_line* (errors)? |} }) -> File
errors <- (({.+}) => error_handler) errors <- (({.+}) => error_handler)
block <- ({ {| statement (%new_line statement)* |} }) -> Block block <- ({ {| statement (%new_line statement)* |} }) -> Block
statement <- ({ (functioncall / expression) }) -> Statement statement <- ({ (functioncall / expression) }) -> Statement
@ -260,37 +273,37 @@ do
functioncall <- ({ {| (expression %word_boundary fn_bits) / (word (%word_boundary fn_bits)?) |} }) -> FunctionCall functioncall <- ({ {| (expression %word_boundary fn_bits) / (word (%word_boundary fn_bits)?) |} }) -> FunctionCall
fn_bit <- (expression / word) fn_bit <- (expression / word)
fn_bits <- fn_bits <-
((".." %ws? (%indent %new_line indented_fn_bits %dedent) (%new_line ".." %ws? fn_bits)?) ((".." %ws? %line_comment? (%indent %new_line indented_fn_bits %dedent) (%new_line ".." %ws? fn_bits)?)
/ (%new_line ".." fn_bit (%word_boundary fn_bits)?) / (%new_line ".." fn_bit (%word_boundary fn_bits)?)
/ (fn_bit (%word_boundary fn_bits)?)) / (fn_bit (%word_boundary fn_bits)?))
indented_fn_bits <- indented_fn_bits <-
fn_bit ((%new_line / %word_boundary) indented_fn_bits)? fn_bit ((%new_line / %word_boundary) indented_fn_bits)?
thunk <- thunk <-
({ ":" %ws? ({ ":" %ws? %line_comment?
((%indent %new_line block ((%dedent (%new_line "..")?) / errors)) ((%indent %new_line block ((%dedent (%new_line "..")?) / errors))
/ (one_liner (%ws? (%new_line? ".."))?)) }) -> Thunk / (one_liner (%ws? (%new_line? ".."))?)) }) -> Thunk
word <- ({ !number {%wordchar+} }) -> Word word <- ({ !number {%wordchar+} }) -> Word
expression <- ({ (longstring / string / number / variable / list / thunk / subexpression) }) -> Expression expression <- ({ (longstring / string / number / variable / list / thunk / subexpression) }) -> Expression
string <- ({ (!longstring) '"' {(("\" .) / [^"])*} '"' }) -> String string <- ({ (!longstring) '"' {(("\" [^%nl]) / [^"%nl])*} '"' }) -> String
longstring <- ({ '".."' %ws? %indent {(%new_line "|" [^%nl]*)+} ((%dedent (%new_line '..')?) / errors) }) -> Longstring longstring <- ({ '".."' %ws? %line_comment? %indent {(%new_line "|" [^%nl]*)+} ((%dedent (%new_line '..')?) / errors) }) -> Longstring
number <- ({ {'-'? [0-9]+ ("." [0-9]+)?} }) -> Number number <- ({ {'-'? [0-9]+ ("." [0-9]+)?} }) -> Number
variable <- ({ ("%" {%wordchar+}) }) -> Var variable <- ({ ("%" {%wordchar+}) }) -> Var
subexpression <- subexpression <-
(!%comment "(" %ws? (functioncall / expression) %ws? ")") ("(" %ws? (functioncall / expression) %ws? ")")
/ ("(..)" %ws? %indent %new_line ((({ {| indented_fn_bits |} }) -> FunctionCall) / expression) %dedent (%new_line "..")?) / ("(..)" %ws? %line_comment? %indent %new_line ((({ {| indented_fn_bits |} }) -> FunctionCall) / expression) %dedent (%new_line "..")?)
list <- ({ {| list <- ({ {|
("[..]" %ws? %indent %new_line indented_list ","? ((%dedent (%new_line "..")?) / errors)) ("[..]" %ws? %line_comment? %indent %new_line indented_list ","? ((%dedent (%new_line "..")?) / errors))
/ ("[" %ws? (list_items ","?)? %ws?"]") / ("[" %ws? (list_items ","?)? %ws?"]")
|} }) -> List |} }) -> List
list_items <- ((functioncall / expression) (list_sep list_items)?) list_items <- ((functioncall / expression) (list_sep list_items)?)
list_sep <- %ws? "," %ws? list_sep <- %ws? "," %ws?
indented_list <- indented_list <-
(functioncall / expression) (((list_sep %new_line?) / %new_line) indented_list)? (functioncall / expression) (((list_sep (%line_comment? %new_line)?) / (%line_comment? %new_line)) indented_list)?
]=] ]=]
lingo = make_parser(lingo) lingo = make_parser(lingo)
local tree = lingo:match(str:gsub("\r", "") .. "\n") local tree = lingo:match(str:gsub("\r", "") .. "\n")

View File

@ -18,16 +18,10 @@ INDENT = " "
lpeg.setmaxstack 10000 -- whoa lpeg.setmaxstack 10000 -- whoa
{:P,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg {:P,:V,:S,:Cg,:C,:Cp,:B,:Cmt} = lpeg
wordchar = P(1)-S(' \t\n\r%:;,.{}[]()"')
comment = re.compile [[comment <- "(#" (comment / ((! "#)") .))* "#)"]]
whitespace = (S(" \t") + comment)^1
nl = P("\n")
blank_line = whitespace^-1 * nl
get_line_indentation = (line)-> get_line_indentation = (line)->
indent_amounts = {[" "]:1, ["\t"]:4} indent_amounts = {[" "]:1, ["\t"]:4}
with sum = 0 with sum = 0
leading_space = line\gsub("([\t ]*).*", "%1") leading_space = line\match("[\t ]*")
for c in leading_space\gmatch "[\t ]" for c in leading_space\gmatch "[\t ]"
sum += indent_amounts[c] sum += indent_amounts[c]
@ -50,10 +44,22 @@ make_parser = (lingo, extra_definitions)->
if num_spaces != indent_stack[#indent_stack] then return nil if num_spaces != indent_stack[#indent_stack] then return nil
return end_pos return end_pos
wordchar = P(1)-S(' \t\n\r%#:;,.{}[]()"')
nl = P("\n")
whitespace = S(" \t")^1
blank_line = whitespace^-1 * nl
line_comment = re.compile([=[ "#" [^%nl]* ]=], {:nl})
block_comment = re.compile([=[
"#.." (!%nl .)* (%indent (!%dedent %nl [^%nl]*)*)
]=], {:nl, :whitespace,
indent:#(nl * blank_line^0 * Cmt(S(" \t")^0, check_indent)),
dedent:#(nl * blank_line^0 * Cmt(S(" \t")^0, check_dedent)),
new_line:nl * blank_line^0 * Cmt(S(" \t")^0, check_nodent)})
blank_line = ((Cmt(whitespace^-1, check_nodent) * (block_comment + line_comment))^-1 + whitespace^-1) * nl
defs = defs =
:wordchar, :nl, ws:whitespace, :comment :wordchar, :nl, ws:whitespace, :blank_line, :block_comment, :line_comment
eol: #nl + (P("")-P(1)) eol: #nl + (P("")-P(1))
word_boundary: whitespace + B(P("..")) + B(S("\";)]")) + #S("\":([") + #((whitespace + nl)^0 * P("..")) word_boundary: whitespace^-1 + B(P("..")) + B(S("\";)]")) + #S("\":([") + #((whitespace + nl)^0 * P(".."))
indent: #(nl * blank_line^0 * Cmt(whitespace^-1, check_indent)) indent: #(nl * blank_line^0 * Cmt(whitespace^-1, check_indent))
dedent: #(nl * blank_line^0 * Cmt(whitespace^-1, check_dedent)) dedent: #(nl * blank_line^0 * Cmt(whitespace^-1, check_dedent))
new_line: nl * blank_line^0 * Cmt(whitespace^-1, check_nodent) new_line: nl * blank_line^0 * Cmt(whitespace^-1, check_nodent)
@ -162,7 +168,7 @@ class NomsuCompiler
if @debug if @debug
print("PARSING:\n#{str}") print("PARSING:\n#{str}")
lingo = [=[ lingo = [=[
file <- ({ {| %ws? %new_line? {:body: block :} %new_line? %ws? (errors)? |} }) -> File file <- ({ {| %blank_line* {:body: block :} %blank_line* (errors)? |} }) -> File
errors <- (({.+}) => error_handler) errors <- (({.+}) => error_handler)
block <- ({ {| statement (%new_line statement)* |} }) -> Block block <- ({ {| statement (%new_line statement)* |} }) -> Block
statement <- ({ (functioncall / expression) }) -> Statement statement <- ({ (functioncall / expression) }) -> Statement
@ -178,37 +184,37 @@ class NomsuCompiler
functioncall <- ({ {| (expression %word_boundary fn_bits) / (word (%word_boundary fn_bits)?) |} }) -> FunctionCall functioncall <- ({ {| (expression %word_boundary fn_bits) / (word (%word_boundary fn_bits)?) |} }) -> FunctionCall
fn_bit <- (expression / word) fn_bit <- (expression / word)
fn_bits <- fn_bits <-
((".." %ws? (%indent %new_line indented_fn_bits %dedent) (%new_line ".." %ws? fn_bits)?) ((".." %ws? %line_comment? (%indent %new_line indented_fn_bits %dedent) (%new_line ".." %ws? fn_bits)?)
/ (%new_line ".." fn_bit (%word_boundary fn_bits)?) / (%new_line ".." fn_bit (%word_boundary fn_bits)?)
/ (fn_bit (%word_boundary fn_bits)?)) / (fn_bit (%word_boundary fn_bits)?))
indented_fn_bits <- indented_fn_bits <-
fn_bit ((%new_line / %word_boundary) indented_fn_bits)? fn_bit ((%new_line / %word_boundary) indented_fn_bits)?
thunk <- thunk <-
({ ":" %ws? ({ ":" %ws? %line_comment?
((%indent %new_line block ((%dedent (%new_line "..")?) / errors)) ((%indent %new_line block ((%dedent (%new_line "..")?) / errors))
/ (one_liner (%ws? (%new_line? ".."))?)) }) -> Thunk / (one_liner (%ws? (%new_line? ".."))?)) }) -> Thunk
word <- ({ !number {%wordchar+} }) -> Word word <- ({ !number {%wordchar+} }) -> Word
expression <- ({ (longstring / string / number / variable / list / thunk / subexpression) }) -> Expression expression <- ({ (longstring / string / number / variable / list / thunk / subexpression) }) -> Expression
string <- ({ (!longstring) '"' {(("\" .) / [^"])*} '"' }) -> String string <- ({ (!longstring) '"' {(("\" [^%nl]) / [^"%nl])*} '"' }) -> String
longstring <- ({ '".."' %ws? %indent {(%new_line "|" [^%nl]*)+} ((%dedent (%new_line '..')?) / errors) }) -> Longstring longstring <- ({ '".."' %ws? %line_comment? %indent {(%new_line "|" [^%nl]*)+} ((%dedent (%new_line '..')?) / errors) }) -> Longstring
number <- ({ {'-'? [0-9]+ ("." [0-9]+)?} }) -> Number number <- ({ {'-'? [0-9]+ ("." [0-9]+)?} }) -> Number
variable <- ({ ("%" {%wordchar+}) }) -> Var variable <- ({ ("%" {%wordchar+}) }) -> Var
subexpression <- subexpression <-
(!%comment "(" %ws? (functioncall / expression) %ws? ")") ("(" %ws? (functioncall / expression) %ws? ")")
/ ("(..)" %ws? %indent %new_line ((({ {| indented_fn_bits |} }) -> FunctionCall) / expression) %dedent (%new_line "..")?) / ("(..)" %ws? %line_comment? %indent %new_line ((({ {| indented_fn_bits |} }) -> FunctionCall) / expression) %dedent (%new_line "..")?)
list <- ({ {| list <- ({ {|
("[..]" %ws? %indent %new_line indented_list ","? ((%dedent (%new_line "..")?) / errors)) ("[..]" %ws? %line_comment? %indent %new_line indented_list ","? ((%dedent (%new_line "..")?) / errors))
/ ("[" %ws? (list_items ","?)? %ws?"]") / ("[" %ws? (list_items ","?)? %ws?"]")
|} }) -> List |} }) -> List
list_items <- ((functioncall / expression) (list_sep list_items)?) list_items <- ((functioncall / expression) (list_sep list_items)?)
list_sep <- %ws? "," %ws? list_sep <- %ws? "," %ws?
indented_list <- indented_list <-
(functioncall / expression) (((list_sep %new_line?) / %new_line) indented_list)? (functioncall / expression) (((list_sep (%line_comment? %new_line)?) / (%line_comment? %new_line)) indented_list)?
]=] ]=]
lingo = make_parser lingo lingo = make_parser lingo