aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authors-ol <s-ol@users.noreply.github.com>2020-01-30 09:44:23 +0000
committers-ol <s-ol@users.noreply.github.com>2020-01-30 09:44:23 +0000
commitf526eb9f1ad140a0566c4f18e19b5ce17d6ea2b1 (patch)
tree708fc4cb5100d62f64231d53954e106e28a5ee99
parentsupport tagging (diff)
downloadalive-f526eb9f1ad140a0566c4f18e19b5ce17d6ea2b1.tar.gz
alive-f526eb9f1ad140a0566c4f18e19b5ce17d6ea2b1.zip
more types &c
-rw-r--r--ast.moon26
-rw-r--r--main.moon87
-rw-r--r--parsing.moon38
-rw-r--r--spec/ast_spec.moon2
-rw-r--r--spec/parsing_spec.moon49
5 files changed, 169 insertions, 33 deletions
diff --git a/ast.moon b/ast.moon
index 39e2f21..4927cbc 100644
--- a/ast.moon
+++ b/ast.moon
@@ -1,3 +1,9 @@
+unescape = (str) ->
+ str = str\gsub '\\"', '"'
+ str = str\gsub "\\'", "'"
+ str = str\gsub "\\\\", "\\"
+ str
+
class Atom
new: (@raw, @style='', @value) =>
@@ -9,12 +15,15 @@ class Atom
@raw
when '"'
"\"#{@raw}\""
+ when "'"
+ "'#{@raw}'"
else
- error!
+ error "unknown atom style: '#{@style}'"
- @make_num: (match) -> Atom match, '', tonumber match
+ @make_num: (match, ...) -> Atom match, '', tonumber match
@make_sym: (match) -> Atom match, '', match
- @make_str: (match) -> Atom match, '"', match
+ @make_strd: (match) -> Atom match, '"', unescape match
+ @make_strq: (match) -> Atom match, "'", unescape match
class Xpr
new: (parts, @style='(', tag) =>
@@ -29,12 +38,13 @@ class Xpr
@tag = tag.value
_walk_sexpr: =>
- if @style == '('
- coroutine.yield @
-
+ -- depth first
for frag in *@
frag\_walk_sexpr!
+ if @style == '('
+ coroutine.yield @
+
walk_sexpr: =>
coroutine.wrap -> @_walk_sexpr!
@@ -48,9 +58,11 @@ class Xpr
switch @style
when 'naked'
buf
- else
+ when '('
tag = if @tag then "[#{@tag}]" else ''
'(' .. tag .. buf .. ')'
+ else
+ error "unknown sexpr style: '#{@style}'"
make_sexpr: (tag, parts) ->
if parts
diff --git a/main.moon b/main.moon
new file mode 100644
index 0000000..a301101
--- /dev/null
+++ b/main.moon
@@ -0,0 +1,87 @@
+lfs = require 'lfs'
+sleep = require 'sleep'
+import program from require 'parsing'
+
+filename = arg[1]
+
+slurp = (file) ->
+ file = io.open file, 'r'
+ with file\read '*all'
+ file\close!
+
+spit = (file, str) ->
+ file = io.open file, 'w'
+ file\write str
+ file\close!
+
+class Runtime
+ spawn: (sexpr) =>
+ print "spawning [#{sexpr.tag}]"
+
+ patch: (new, old) =>
+ -- print "patching [#{new.tag}]"
+
+ destroy: (sexpr) =>
+ print "destroying [#{sexpr.tag}]"
+
+runtime = Runtime!
+
+class Registry
+ new: =>
+ @last_tag = 0
+ @map = {}
+
+ spawn: (sexpr) =>
+ @last_tag += 1
+
+ sexpr.tag or= @last_tag
+ @map[sexpr.tag] = sexpr
+
+ runtime\spawn sexpr
+ sexpr.tag
+
+ patch: (sexpr) =>
+ old = @map[sexpr.tag]
+
+ if not old
+ return @spawn sexpr
+
+ @map[sexpr.tag] = sexpr
+
+ runtime\patch sexpr, old
+ sexpr.tag
+
+ patch_root: (root) =>
+ seen = {}
+
+ for sexpr in root\walk_sexpr!
+ tag = if not sexpr.tag
+ @spawn sexpr
+ else
+ @patch sexpr
+ seen[tag] = true
+
+ for tag, expr in pairs @map
+ if not seen[tag]
+ runtime\destroy expr
+ @map[tag] = nil
+
+registry = Registry!
+last_modification = 0
+
+while true
+ sleep 10
+
+ { :mode, :modification } = (lfs.attributes filename) or {}
+ if mode != 'file'
+ return
+
+ if last_modification >= modification
+ continue
+
+ last_modification = modification
+
+ root = assert (program\match slurp filename), "error parsing"
+ registry\patch_root root
+
+ spit filename, root\stringify!
diff --git a/parsing.moon b/parsing.moon
index d4fba0b..9466d9e 100644
--- a/parsing.moon
+++ b/parsing.moon
@@ -1,26 +1,36 @@
-lpeg = require 'lpeg'
import Atom, Xpr from require 'ast'
+import R, S, P, V, C, Ct from require 'lpeg'
-space = (lpeg.S ' \t\r\n') ^ 1 / 1
-mspace = (lpeg.S ' \t\r\n') ^ 0 / 1
+-- whitespace
+space = (S ' \t\r\n') ^ 1 / 1 -- required whitespace
+mspace = (S ' \t\r\n') ^ 0 / 1 -- optional whitespace
-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
+-- atoms
+sym = ((R 'az', 'AZ') + (S '-_+*/.!?')) ^ 1 / Atom.make_sym
-expr = (lpeg.V 'sexpr') + atom
-explist = lpeg.Ct mspace * (lpeg.V 'expr') * (space * (lpeg.V 'expr')) ^ 0 * mspace
+strd = '"' * (C ((P'\\"') + (P '\\\\') + (1 - P '"')) ^ 0) * '"' / Atom.make_strd
+strq = "'" * (C ((P"\\'") + (P '\\\\') + (1 - P "'")) ^ 0) * "'" / Atom.make_strq
+str = strd + strq
-tag = (lpeg.P '[') * num * (lpeg.P ']')
-sexpr = (lpeg.P '(') * tag^-1 * (lpeg.V 'explist') * (lpeg.P ')') / Xpr.make_sexpr
+digit = R '09'
+int = digit^1
+float = (digit^1 * '.' * digit^0) + (digit^0 * '.' * digit^1)
+num = (float + int) / Atom.make_num
-nexpr = lpeg.P {
- (lpeg.V 'explist') / Xpr.make_nexpr
+atom = num + sym + str
+
+expr = (V 'sexpr') + atom
+explist = Ct mspace * (V 'expr') * (space * (V 'expr')) ^ 0 * mspace
+
+tag = (P '[') * num * (P ']')
+sexpr = (P '(') * tag^-1 * (V 'explist') * (P ')') / Xpr.make_sexpr
+
+nexpr = P {
+ (V 'explist') / Xpr.make_nexpr
:expr, :explist, :sexpr
}
-sexpr = lpeg.P {
+sexpr = P {
'sexpr'
:expr, :explist, :sexpr
}
diff --git a/spec/ast_spec.moon b/spec/ast_spec.moon
index 402ce03..cfb64cf 100644
--- a/spec/ast_spec.moon
+++ b/spec/ast_spec.moon
@@ -10,6 +10,6 @@ describe 'Xpr', ->
root = Xpr { '', ac, ' ', b, '' }, 'naked'
iter = root\walk_sexpr!
- for res in *{ ac, b, aa, ab }
+ for res in *{ ac, aa, ab, b }
assert.is.equal res, iter!
assert.is.nil iter!
diff --git a/spec/parsing_spec.moon b/spec/parsing_spec.moon
index 5e6e1fc..7e8e5a7 100644
--- a/spec/parsing_spec.moon
+++ b/spec/parsing_spec.moon
@@ -7,17 +7,44 @@ describe 'atom parsing', ->
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!
+ describe 'numbers', ->
+ it 'parses ints', ->
+ num = atom\match '1234 nope'
+ assert.is.equal '1234', num.raw
+ assert.is.equal 1234, num.value
+ assert.is.equal '1234', num\stringify!
+
+ it 'parses floats', ->
+ num = atom\match '0.123 nope'
+ assert.is.equal 0.123, num.value
+ assert.is.equal '0.123', num\stringify!
+
+ num = atom\match '.123 nope'
+ assert.is.equal .123, num.value
+ assert.is.equal '.123', num\stringify!
+
+ num = atom\match '0. nope'
+ assert.is.equal 0, num.value
+ assert.is.equal '0.', num\stringify!
+
+ describe 'strings', ->
+ it 'parses double-quote 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!
+
+ it 'parses single-quote 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!
+
+ it 'handles escapes', ->
+ str = atom\match '"string with \\"quote\\"s"'
+ assert.is.equal 'string with \\"quote\\"s', str.raw
+ assert.is.equal 'string with "quote"s', str.value
+ assert.is.equal '"string with \\"quote\\"s"', str\stringify!
test 'whitespace parsing', ->
assert.is.equal ' ', space\match ' '