diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-01-30 09:44:23 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-01-30 09:44:23 +0000 |
| commit | f526eb9f1ad140a0566c4f18e19b5ce17d6ea2b1 (patch) | |
| tree | 708fc4cb5100d62f64231d53954e106e28a5ee99 | |
| parent | support tagging (diff) | |
| download | alive-f526eb9f1ad140a0566c4f18e19b5ce17d6ea2b1.tar.gz alive-f526eb9f1ad140a0566c4f18e19b5ce17d6ea2b1.zip | |
more types &c
| -rw-r--r-- | ast.moon | 26 | ||||
| -rw-r--r-- | main.moon | 87 | ||||
| -rw-r--r-- | parsing.moon | 38 | ||||
| -rw-r--r-- | spec/ast_spec.moon | 2 | ||||
| -rw-r--r-- | spec/parsing_spec.moon | 49 |
5 files changed, 169 insertions, 33 deletions
@@ -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 ' ' |
