diff options
| author | s-ol <s-ol@users.noreply.github.com> | 2020-02-01 18:33:18 +0000 |
|---|---|---|
| committer | s-ol <s-ol@users.noreply.github.com> | 2020-02-01 19:15:05 +0000 |
| commit | 2aa7aa6113f810e5fbe57abc985b05e3d58e4961 (patch) | |
| tree | ed9b2a4563ed3132f6ee56ca858ed25e0ed92752 | |
| parent | more flexible AST\walk (diff) | |
| download | alive-2aa7aa6113f810e5fbe57abc985b05e3d58e4961.tar.gz alive-2aa7aa6113f810e5fbe57abc985b05e3d58e4961.zip | |
basic references
| -rw-r--r-- | ast.moon | 81 | ||||
| -rw-r--r-- | parsing.moon | 2 | ||||
| -rw-r--r-- | registry.moon | 11 | ||||
| -rw-r--r-- | spec/ast_spec.moon | 18 | ||||
| -rw-r--r-- | spec/parsing_spec.moon | 116 | ||||
| -rw-r--r-- | test.alv | 6 |
6 files changed, 130 insertions, 104 deletions
@@ -7,36 +7,68 @@ unescape = (str) -> str = str\gsub "\\\\", "\\" str +class ASTNode + -- first pass (outin): + -- * expand macros + -- * define scoped values + -- * evaluate references + expand: (scope) => + + -- second pass (inout): + -- * setup OPs (spawn/patch) + link: => + class Atom type: 'Atom' - new: (@raw, @style='', value) => - @value = Const value + new: (@raw, @atom_type) => + + expand: (scope) => + @value = Const switch @atom_type + when 'num' + tonumber @raw + when 'sym' + assert scope[@raw], "undefined reference to symbol '#{@raw}'" + when 'strq', 'std' + unescape @raw + else + error "unknown atom type: '#{@atom_type}'" + + link: => _walk: => coroutine.yield @type, @ stringify: => - switch @style - when '' + switch @atom_type + when 'sym', 'num' @raw - when '"' - "\"#{@raw}\"" - when "'" + when 'strq' "'#{@raw}'" + when 'strd' + "\"#{@raw}\"" else - error "unknown atom style: '#{@style}'" + error "unknown atom type: '#{@atom_type}'" - @make_num: (match, ...) -> Atom match, '', tonumber match - @make_sym: (match) -> Atom match, '', match - @make_strd: (match) -> Atom match, '"', unescape match - @make_strq: (match) -> Atom match, "'", unescape match + @make_num: (match) -> Atom match, 'num' + @make_sym: (match) -> Atom match, 'sym' + @make_strd: (match) -> Atom match, 'strd' + @make_strq: (match) -> Atom match, 'strq' __tostring: => @stringify! class Xpr type: 'Xpr' - - new: (parts, @style='(', tag) => + + -- either: + -- * style, tag, parts + -- * style, parts + new: (@style, tag, parts) => + if not parts + parts = tag + tag = nil + + @tag = tag + @white = {} @white[0] = parts[1] @@ -44,8 +76,12 @@ class Xpr @[i/2] = parts[i] @white[i/2] = parts[i+1] - if tag - @tag = tag.value\getc! + expand: (scope) => + + link: => + + head: => @[1].value + tail: => unpack [p.value for p in *@[2,]] _walk: (dir, yield_self=true) => coroutine.yield @type, @ if yield_self and dir == 'outin' @@ -55,10 +91,6 @@ class Xpr coroutine.yield @type, @ if yield_self and dir == 'inout' - - head: => @[1].value - tail: => unpack [p.value for p in *@[2,]] - walk: (dir, yield_self=true) => assert dir == 'inout' or dir == 'outin', "dir has to be either inout or outin" coroutine.wrap -> @_walk dir, yield_self @@ -79,13 +111,8 @@ class Xpr else error "unknown sexpr style: '#{@style}'" - make_sexpr: (tag, parts) -> - if parts - Xpr parts, '(', tag - else - Xpr tag, '(' - - make_nexpr: (parts) -> Xpr parts, 'naked' + make_sexpr: (...) -> Xpr '(', ... + make_nexpr: (...) -> Xpr 'naked', ... __tostring: => if @tag diff --git a/parsing.moon b/parsing.moon index 1110b42..8e53ec6 100644 --- a/parsing.moon +++ b/parsing.moon @@ -28,7 +28,7 @@ atom = num + sym + str expr = (V 'sexpr') + atom explist = Ct mspace * (V 'expr') * (space * (V 'expr'))^0 * mspace -tag = (P '[') * num * (P ']') +tag = (P '[') * (int / tonumber) * (P ']') sexpr = (P '(') * tag^-1 * (V 'explist') * (P ')') / Xpr.make_sexpr nexpr = P { diff --git a/registry.moon b/registry.moon index 394267c..950ec38 100644 --- a/registry.moon +++ b/registry.moon @@ -25,6 +25,10 @@ class Registry seen = {} to_tag = {} + for typ, atom in @root\walk 'outin', false + continue unless typ == 'Atom' + atom\expand @globals + for typ, sexpr in @root\walk 'inout', false continue unless typ == 'Xpr' @@ -47,13 +51,12 @@ class Registry @map[tag] = nil spawn_expr: (sexpr) => - ref = sexpr\head!\getc! + def = sexpr\head!\getc! if sexpr.tag - print "respawning [#{sexpr.tag}]: '#{ref}'" + print "respawning [#{sexpr.tag}]: '#{def}'" else - print "spawning '#{ref}'" + print "spawning '#{def}'" - def = assert @globals[ref], "no such function: '#{ref}'" sexpr.value = def sexpr patch_expr: (new, old) => diff --git a/spec/ast_spec.moon b/spec/ast_spec.moon index 80fbb56..00f0c29 100644 --- a/spec/ast_spec.moon +++ b/spec/ast_spec.moon @@ -2,15 +2,15 @@ import Atom, Xpr from require 'ast' describe 'AST', -> describe 'can be walked', -> - a1 = Atom '1', '', 1 - a2 = Atom '2', '', 2 - a3 = Atom '3', '', 3 - x1 = Xpr { '', a1, '' } - x21 = Xpr { '', a2, '' } - x22 = Xpr { '', a3, '' } - x2 = Xpr { '', x21, ' ', x22, '' } - - root = Xpr { '', x1, ' ', x2, '' }, 'naked' + a1 = Atom.make_num '1' + a2 = Atom.make_num '2' + a3 = Atom.make_num '3' + x1 = Xpr '(', { '', a1, '' } + x21 = Xpr '(', { '', a2, '' } + x22 = Xpr '(', { '', a3, '' } + x2 = Xpr '(', { '', x21, ' ', x22, '' } + + root = Xpr 'naked', { '', x1, ' ', x2, '' } assert_yields = (expected_order, iter) -> for val in *expected_order diff --git a/spec/parsing_spec.moon b/spec/parsing_spec.moon index 5af12ba..04b4741 100644 --- a/spec/parsing_spec.moon +++ b/spec/parsing_spec.moon @@ -1,127 +1,121 @@ import space, atom, expr, explist, sexpr, nexpr, program, comment from require 'parsing' +verify_parse = (parser, str) -> + with assert parser\match str + assert.is.equal str, \stringify! + +verify_parse_nope = (parser, str) -> + with assert parser\match str + without_nope = str\match '^(.*) nope$' + assert.is.equal without_nope, \stringify! + describe 'atom parsing', -> test 'symbols', -> - sym = atom\match 'some-toast help' + sym = verify_parse_nope atom, 'some-toast nope' + assert.is.equal 'sym', sym.atom_type assert.is.equal 'some-toast', sym.raw - assert.is.equal 'some-toast', sym.value\getc! assert.is.equal 'some-toast', sym\stringify! describe 'numbers', -> it 'parses ints', -> - num = atom\match '1234 nope' + num = verify_parse_nope atom, '1234 nope' + assert.is.equal 'num', num.atom_type assert.is.equal '1234', num.raw - assert.is.equal 1234, num.value\getc! assert.is.equal '1234', num\stringify! it 'parses floats', -> - num = atom\match '0.123 nope' - assert.is.equal 0.123, num.value\getc! + num = verify_parse_nope atom, '0.123 nope' + assert.is.equal 'num', num.atom_type assert.is.equal '0.123', num\stringify! - num = atom\match '.123 nope' - assert.is.equal .123, num.value\getc! + num = verify_parse_nope atom, '.123 nope' + assert.is.equal 'num', num.atom_type assert.is.equal '.123', num\stringify! - num = atom\match '0. nope' - assert.is.equal 0, num.value\getc! + num = verify_parse_nope atom, '0. nope' + assert.is.equal 'num', num.atom_type assert.is.equal '0.', num\stringify! describe 'strings', -> it 'parses double-quote strings', -> - str = atom\match '"help some stuff!" nope' + str = verify_parse_nope atom, '"help some stuff!" nope' + assert.is.equal 'strd', str.atom_type assert.is.equal 'help some stuff!', str.raw - assert.is.equal 'help some stuff!', str.value\getc! assert.is.equal '"help some stuff!"', str\stringify! it 'parses single-quote strings', -> - str = atom\match "'help some stuff!' nope" + str = verify_parse_nope atom, "'help some stuff!' nope" + assert.is.equal 'strq', str.atom_type assert.is.equal "help some stuff!", str.raw - assert.is.equal "help some stuff!", str.value\getc! assert.is.equal "'help some stuff!'", str\stringify! it 'handles escapes', -> - str = atom\match '"string with \\"quote\\"s"' + str = verify_parse_nope atom, '"string with \\"quote\\"s" nope' + assert.is.equal 'strd', str.atom_type assert.is.equal 'string with \\"quote\\"s', str.raw - assert.is.equal 'string with "quote"s', str.value\getc! assert.is.equal '"string with \\"quote\\"s"', 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\getc! - assert.is.equal 'ok-yes', node[2].value\getc! - - assert.is.equal ' 3\tok-yes', node\stringify! - - it 'handles trailing whitespace', -> - node = nexpr\match '3\tok-yes\n' + describe 'handles whitespace', -> + verify = (str) -> + node = verify_parse nexpr, str - assert.is.equal 2, #node - assert.is.equal 3, node[1].value\getc! - assert.is.equal 'ok-yes', node[2].value\getc! - - assert.is.equal '3\tok-yes\n', node\stringify! + assert.is.equal 'naked', node.style + assert.is.equal 2, #node + assert.is.equal '3', node[1].raw + assert.is.equal 'ok-yes', node[2].raw - it 'handles whitespace everywhere', -> - node = nexpr\match ' 3\tok-yes\n' + it 'at the front of the string', -> + verify ' 3\tok-yes' - assert.is.equal 2, #node - assert.is.equal 3, node[1].value\getc! - assert.is.equal 'ok-yes', node[2].value\getc! + it 'at the end of the string', -> + verify ' 3\tok-yes\n' - assert.is.equal ' 3\tok-yes\n', node\stringify! + it 'everywhere', -> + verify ' 3\tok-yes\n' describe 'sexpr', -> test 'basic parsing', -> - str = '( 3 ok-yes - "friend" )' - node = sexpr\match str + node = verify_parse sexpr, '( 3 ok-yes + "friend" )' assert.is.equal '(', node.style assert.is.equal 3, #node - assert.is.equal 3, node[1].value\getc! - assert.is.equal 'ok-yes', node[2].value\getc! - assert.is.equal 'friend', node[3].value\getc! - - assert.is.equal str, node\stringify! + assert.is.equal '3', node[1].raw + assert.is.equal 'ok-yes', node[2].raw + assert.is.equal 'friend', node[3].raw test 'tag parsing', -> - str = '([42]tagged 2)' - node = sexpr\match str + node = verify_parse sexpr, '([42]tagged 2)' assert.is.equal '(', node.style assert.is.equal 2, #node - assert.is.equal 'tagged', node[1].value\getc! - assert.is.equal 2, node[2].value\getc! assert.is.equal 42, node.tag - assert.is.equal str, node\stringify! + +test 'whitespace', -> + assert.is.equal ' ', space\match ' ' + assert.is.equal '\n\t ', space\match '\n\t ' describe 'comments', -> comment = comment / 1 - test 'simple parsing', -> + it 'are parsed', -> str = '#(this is a comment)' assert.is.equal str, comment\match str - test 'nested parsing', -> - str = '#(this is a comment (with nested parenthesis))' + it 'extend to matching braces', -> + str = '#(this is a comment #(with nested comments))' assert.is.equal str, comment\match str - str = '#(this is a comment #(with nested comments))' + it 'can nest', -> + str = '#(this is a comment (with nested parenthesis))' assert.is.equal str, comment\match str describe 'resynthesis', -> test 'mixed parsing', -> str = '( 3 ok-yes "friend" )' - node = program\match str + node = verify_parse program, str assert.is.equal str, node\stringify! test 'complex', -> @@ -141,5 +135,5 @@ describe 'resynthesis', -> (random-rot) ) ) ' - matched = assert.is.truthy program\match str + matched = assert.is.truthy verify_parse program, str assert.is.equal str, matched\stringify! @@ -1,2 +1,4 @@ -([2]osc/out '127.0.0.1' 9000 '/param/radius/set' ([1]time/lfo 1)) -([4]osc/out '127.0.0.1' 9000 '/param/offset/set' ([3]time/lfo 1.0 'saw')) + +([6]osc/out '127.0.0.1' 9000 '/param/radius/set' ([1]time/lfo 0.5)) +([7]osc/out '127.0.0.1' 9000 '/param/offset/set' ([5]time/lfo 1.2 'saw')) + |
