aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-01-29 21:49:09 +0000
committers-ol <s-ol@users.noreply.github.com>2020-01-29 21:49:39 +0000
commit3d4b6213c1e37c695ca3f33347b8b041825ca471 (patch)
treeccf52664d6397a11bdcc8b9c81c8da759d50b8c3
downloadalive-3d4b6213c1e37c695ca3f33347b8b041825ca471.tar.gz
alive-3d4b6213c1e37c695ca3f33347b8b041825ca471.zip
initial commit
-rw-r--r--parsing.moon76
-rw-r--r--spec/peg_spec.moon71
2 files changed, 147 insertions, 0 deletions
diff --git a/parsing.moon b/parsing.moon
new file mode 100644
index 0000000..f9efef4
--- /dev/null
+++ b/parsing.moon
@@ -0,0 +1,76 @@
+lpeg = require 'lpeg'
+
+class Atom
+ new: (@raw, @style='', @value) =>
+
+ stringify: =>
+ switch @style
+ when ''
+ @raw
+ when '"'
+ "\"#{@raw}\""
+ else
+ error!
+
+ @make_num: (match) -> Atom match, '', tonumber match
+ @make_sym: (match) -> Atom match, '', match
+ @make_str: (match) -> Atom match, '"', match
+
+class Xpr
+ new: (parts, @style='(') =>
+ @white = {}
+ @white[0] = parts[1]
+
+ for i = 2,#parts,2
+ @[i/2] = parts[i]
+ @white[i/2] = parts[i+1]
+
+ stringify: =>
+ buf = ''
+ buf ..= @white[0]
+ for i, frag in ipairs @
+ buf ..= frag\stringify!
+ buf ..= @white[i]
+
+ switch @style
+ when 'naked'
+ buf
+ else
+ '(' .. buf .. ')'
+
+ make_sexpr: (parts) -> Xpr parts, '('
+ make_nexpr: (parts) -> Xpr parts, 'naked'
+
+space = (lpeg.S ' \t\r\n') ^ 1 / 1
+mspace = (lpeg.S ' \t\r\n') ^ 0 / 1
+
+sym = ((lpeg.R 'az', 'AZ') + (lpeg.S '-_+*')) ^ 1 / Atom.make_sym
+str = '"' * (lpeg.C (1 - lpeg.P '"') ^ 0) * '"' / Atom.make_str
+num = (lpeg.R '09', 'AZ') ^ 1 / Atom.make_num
+atom = sym + num + str
+
+expr = (lpeg.V 'sexpr') + atom
+explist = lpeg.Ct mspace * (lpeg.V 'expr') * (space * atom) ^ 0 * mspace
+sexpr = (lpeg.P '(') * (lpeg.V 'explist') * (lpeg.P ')') / Xpr.make_sexpr
+
+nexpr = lpeg.P {
+ (lpeg.V 'explist') / Xpr.make_nexpr
+ :expr, :explist, :sexpr
+}
+
+sexpr = lpeg.P {
+ 'sexpr'
+ :expr, :explist, :sexpr
+}
+
+program = nexpr
+
+{
+ :space
+ :atom
+ :expr
+ :explist
+ :sexpr
+ :nexpr
+ :program
+}
diff --git a/spec/peg_spec.moon b/spec/peg_spec.moon
new file mode 100644
index 0000000..1b25406
--- /dev/null
+++ b/spec/peg_spec.moon
@@ -0,0 +1,71 @@
+import space, atom, expr, explist, sexpr, nexpr, program from require 'parsing'
+
+describe 'atom parsing', ->
+ test 'symbols', ->
+ sym = atom\match 'some-toast help'
+ assert.is.equal 'some-toast', sym.raw
+ assert.is.equal 'some-toast', sym.value
+ assert.is.equal 'some-toast', sym\stringify!
+
+ test 'numbers', ->
+ num = atom\match '1234 nope'
+ assert.is.equal '1234', num.raw
+ assert.is.equal 1234, num.value
+ assert.is.equal '1234', num\stringify!
+
+ test 'strings', ->
+ str = atom\match '"help some stuff!" nope'
+ assert.is.equal 'help some stuff!', str.raw
+ assert.is.equal 'help some stuff!', str.value
+ assert.is.equal '"help some stuff!"', str\stringify!
+
+test 'whitespace parsing', ->
+ assert.is.equal ' ', space\match ' '
+ assert.is.equal '\n\t ', space\match '\n\t '
+
+describe 'nexpr parsing', ->
+ it 'handles leading whitespace', ->
+ node = nexpr\match ' 3\tok-yes'
+
+ assert.is.equal 2, #node
+ assert.is.equal 3, node[1].value
+ assert.is.equal 'ok-yes', node[2].value
+
+ assert.is.equal ' 3\tok-yes', node\stringify!
+
+ it 'handles trailing whitespace', ->
+ node = nexpr\match '3\tok-yes\n'
+
+ assert.is.equal 2, #node
+ assert.is.equal 3, node[1].value
+ assert.is.equal 'ok-yes', node[2].value
+
+ assert.is.equal '3\tok-yes\n', node\stringify!
+
+ it 'handles whitespace everywhere', ->
+ node = nexpr\match ' 3\tok-yes\n'
+
+ assert.is.equal 2, #node
+ assert.is.equal 3, node[1].value
+ assert.is.equal 'ok-yes', node[2].value
+
+ assert.is.equal ' 3\tok-yes\n', node\stringify!
+
+test 'sexpr parsing', ->
+ str = '( 3 ok-yes
+ "friend" )'
+ node = sexpr\match str
+
+ assert.is.equal '(', node.style
+ assert.is.equal 3, #node
+ assert.is.equal 3, node[1].value
+ assert.is.equal 'ok-yes', node[2].value
+ assert.is.equal 'friend', node[3].value
+
+ assert.is.equal str, node\stringify!
+
+test 'mixed parsing', ->
+ str = '( 3 ok-yes
+ "friend" )'
+ node = program\match str
+ assert.is.equal str, node\stringify!