123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880 |
- 'use strict';
- class ParserError extends Error {}
- function parser_error (element, info) {
- check(parser_error, arguments, {
- element: Any, // $u(Token.Valid, SyntaxTreeNode),
- info: Str
- })
- function get_message () {
- function get_token (tree) {
- assert(is_not(tree, SyntaxTreeEmpty))
- return transform(tree, [
- { when_it_is: SyntaxTreeLeaf, use: t => t.children },
- { when_it_is: Otherwise, use: t => get_token(t.children[0]) }
- ])
- }
- let token = transform(element, [
- { when_it_is: Token.Valid, use: x => x },
- { when_it_is: Otherwise, use: x => get_token(x) }
- ])
- let { row, col } = token.position
- return `row ${row}, column ${col}: '${token.matched.name}': ${info}`
- }
- return {
- assert: function (condition) {
- this.if(!condition)
- },
- if: function (condition) {
- if (condition) { this.throw() }
- },
- throw: function () {
- throw ( new ParserError(get_message()) )
- }
- }
- }
- function get_tokens (string) {
- check(get_tokens, arguments, { string: Str })
- function check_parentheses (tokens) {
- let union = (...list) => $u.apply({}, map(list, x => Token(x)))
- let right_of = {
- '(' : ')', '[' : ']', '{' : '}', '.[': ']', '.{' : '}',
- '..[': ']', '..{': '}', '...{': '}'
- }
- let left = union('(', '[', '{', '.[', '.{', '..[', '..{', '...{')
- let right = union(')', ']', '}')
- let all = $u(left, right)
- let parentheses = filter_lazy(tokens, token => is(token, all))
- function top (stack) {
- assert(stack.length > 0)
- return stack[stack.length-1]
- }
- function check_error (stack) {
- let msg = 'missing coressponding parentheses'
- if (stack.length > 0) {
- parser_error(top(stack).token, msg).throw()
- }
- }
- check_error(fold(parentheses, [], function (token, stack) {
- let name = token.matched.name
- let pos = token.position
- let element = { name: name, pos: pos, token: token }
- let pushed = added(stack, element)
- let poped = stack.slice(0, stack.length-1)
- let empty = $(stack => stack.length == 0)
- let corresponding = $(stack => right_of[top(stack).name] == name)
- return transform(token, [
- { when_it_is: left, use: t => pushed },
- { when_it_is: right, use: t => transform(stack, [
- { when_it_is: empty, use: s => BreakWith(pushed) },
- { when_it_is: corresponding, use: s => poped },
- { when_it_is: Otherwise, use: s => Break }
- ])}
- ])
- }))
- }
- function assert_valid (tokens) {
- return map_lazy(tokens, t => (assert(is(t, Token.Valid)) && t))
- }
- function remove_comment (tokens) {
- /* remove ordinary comment and inline comment */
- let Current = name => $(look => is(look.current, Token(name)))
- let Left = name => $(look => is(look.left, Token(name)))
- let InlineCommentElement = $u(
- Current('..'),
- $n(Left('..'), Current('Name'))
- )
- return apply_on(tokens, chain(
- x => filter_lazy(x, t => is_not(t, Token('Comment'))),
- x => lookaside(x, Token.Null),
- x => filter_lazy(x, look => is_not(look, InlineCommentElement)),
- x => map_lazy(x, look => look.current)
- ))
- }
- function remove_space (tokens) {
- /**
- * note: linefeed won't be removed by this function.
- * therefore, the effect of add_call_operator()
- * and add_get_operator() cannot cross lines.
- */
- return filter_lazy(tokens, token => token.matched.name != 'Space')
- }
- function add_call_operator (tokens) {
- return insert(tokens, Token.Null, function (token, next) {
- let this_is_id = is(token, Token('Name'))
- let this_is_rp = is(token, Token(')'))
- let next_is_lp = is(next, Token('('))
- let need_insert = (this_is_id || this_is_rp) && next_is_lp
- return (
- need_insert?
- Token.create_from(token, 'Operator', 'Call', '@'): Nothing
- )
- })
- }
- function add_get_operator (tokens) {
- return insert(tokens, Token.Null, function (token, next) {
- let this_is_id = is(token, Token('Name'))
- let next_is_lb = is(next, Token('['))
- let need_insert = (this_is_id && next_is_lb)
- return (
- need_insert?
- Token.create_from(token, 'Operator', 'Get', '#'): Nothing
- )
- })
- }
- function eliminate_ambiguity (name) {
- let mapper = ({
- '.': function (left, token, right) {
- let par = $u(Token(')'), Token(']'), Token('}'))
- let left_is_op = is(left, $d(Token.Operator, par))
- let left_is_lf = is(left, Token('Linefeed'))
- let name = (left_is_op || left_is_lf)? 'Parameter': 'Access'
- return Token.create_from(token, 'Operator', name)
- },
- Name: function (left, token, right) {
- let left_is_access = is(left, Token('Access'))
- let name = left_is_access? 'Member': 'Identifier'
- return Token.create_from(token, 'Name', name)
- }
- })[name]
- return function (tokens) {
- function transform (item) {
- let { left, current, right } = item
- let token = current
- let current_name = token.matched.name
- return (name == current_name)?
- mapper(left, token, right): token
- }
- return map_lazy(lookaside(tokens, Token.Null), transform)
- }
- }
- function remove_linefeed (tokens) {
- return filter_lazy(tokens, token => token.matched.name != 'Linefeed')
- }
- let raw = list(CodeScanner(string).match(Matcher(Tokens)))
- check_parentheses(assert_valid(raw))
- return apply_on(raw, chain(
- remove_comment,
- remove_space,
- add_call_operator,
- add_get_operator,
- eliminate_ambiguity('.'),
- eliminate_ambiguity('Name'),
- remove_linefeed,
- assert_valid,
- list
- ))
- }
- const SyntaxTreeEmpty = Struct({
- name: Str,
- children: $n(List, $(a => a.length == 0))
- })
- const SyntaxTreeLeaf = Struct({
- name: Str,
- children: Token.Valid
- })
- const SyntaxTreeNode = $u(SyntaxTreeLeaf, Struct({
- name: Str,
- children: ListOf( $u(SyntaxTreeLeaf, $(x => is(x, SyntaxTreeNode))) )
- }))
- const SyntaxTreeRoot = $n(SyntaxTreeNode, Struct({
- ok: Bool,
- amount: Int
- }))
- function print_tree (tree, deepth = 0, is_last = [true]) {
- let indent_increment = 2
- let repeat = function (string, n, f = (i => '')) {
- return join(map_lazy(count(n), i => f(i) || string), '')
- }
- let base = repeat(' ', indent_increment)
- let indent = repeat(base, deepth,
- i => (
- is_last[i]?
- repeat(' ', indent_increment):
- ('│'+repeat(' ', indent_increment-1))
- )
- )
- let pointer = (
- ((is_last[deepth])? '└': '├')
- + repeat('─', indent_increment-1)
- + ((tree.children.length > 0)? '┬': '─')
- + '─'
- )
- let node_name = (
- is(tree.time, Num)?
- `${tree.name} (${tree.time.toFixed(0)})`:
- `${tree.name}`
- )
- let node_children = transform(tree, [
- { when_it_is: SyntaxTreeEmpty, use: t => (' ' + '[]') },
- { when_it_is: SyntaxTreeLeaf, use: function (tree) {
- let string = tree.children.matched.string
- return (string == tree.name)? '': `: ${string}`
- } },
- { when_it_is: Otherwise, use: function (tree) {
- let last_index = tree.children.length-1
- let subtree_string = apply_on(tree.children, chain(
- x => map_lazy(x, (child, index) => print_tree(
- child, deepth+1,
- added(is_last, index == last_index)
- )),
- x => join(x, LF)
- ))
- return (LF + subtree_string)
- } }
- ])
- return (indent + pointer + node_name + node_children)
- }
- function build_leaf (token) {
- check(build_leaf, arguments, { token: Token.Valid })
- return { amount: 1, name: token.matched.name, children: token }
- }
- function wipe_list(list) {
- while(list.length > 0) {
- list.pop()
- }
- }
- function copy_list(dest, source) {
- for(let element of source) {
- dest.push(element)
- }
- }
- let f_count = count
- function match_part (syntax, part_list, tokens, part_name, pos) {
- let N = 32
- let buffer = new Int32Array(new ArrayBuffer(10000*N*4))
- let saved = new Int32Array(new ArrayBuffer(100000*N*4))
- let names = list(cat([null], part_list))
- let id_of_name = fold(names, {}, (e,v,i) => (v[e]=i, v))
- let key = {
- name: 0,
- amount: 1,
- parent: 2,
- pos: 3,
- deriv: 4,
- n: 5,
- }
- let offset = 6
- let cur = 0
- let count = 0
-
- function watch() {
- let l = []
- for(let i=0; i<=cur; i++) {
- l.push({
- name: names[buffer[i*N + key.name]],
- amount: buffer[i*N + key.amount],
- parent: buffer[i*N + key.parent],
- pos: buffer[i*N + key.pos],
- deriv: buffer[i*N + key.deriv],
- children: map(f_count(buffer[i*N + key.n]), j => buffer[i*N + offset + j])
- })
- }
- return l
- }
-
- /*
- function push(name, parent) {
- cur += 1
- let top = N*(cur)
- buffer[top + key.name] = id_of_name[name]
- buffer[top + key.amount] = 0
- buffer[top + key.parent] = parent
- buffer[top + key.pos] = -1
- buffer[top + key.deriv] = 0
- buffer[top + key.n] = 0
- }
-
- function pop() {
- let top = N*cur
- let parent = N*buffer[top + key.parent]
- if (top != 0) {
- buffer[parent + key.amount] += buffer[top + key.amount]
- buffer[parent + offset + buffer[parent + key.n]] = count
- buffer[parent + key.n] += 1
- }
- let new_saved = N*count
- saved[new_saved + key.name] = buffer[top + key.name]
- saved[new_saved + key.amount] = buffer[top + key.amount]
- saved[new_saved + key.n] = buffer[top + key.n]
- let i = 0
- while(i < buffer[top + key.n]) {
- saved[new_saved + offset + i] = buffer[top + offset + i]
- i++
- }
- count += 1
- cur -= 1
- }
-
- function rollback() {
- let i_parent = buffer[N*cur + key.parent]
- buffer[N*i_parent + key.deriv] += 1
- cur = i_parent
- }
- */
-
- buffer[N*0 + key.name] = 0
- buffer[key.amount] = 0
- buffer[key.parent] = -1
- buffer[key.pos] = pos
- buffer[key.deriv] = 0
- buffer[key.n] = 0
- cur += 1
- buffer[N*cur + key.name] = id_of_name[part_name]
- buffer[N + key.amount] = 0
- buffer[N + key.parent] = 0
- buffer[N + key.pos] = pos
- buffer[N + key.deriv] = 0
- buffer[N + key.n] = 0
-
- let i = 0
-
- while (cur > 0) {
- var top = N * cur
- var parent = N * buffer[top + key.parent]
- var pos = buffer[top + key.pos]
- if (pos == -1) {
- pos = buffer[parent + key.pos] + buffer[parent + key.amount]
- buffer[top + key.pos] = pos
- }
- //var token = (pos < tokens.length)? tokens[pos]: Token.Null
- //var part = names[buffer[top + key.name]]
- assert(names[buffer[top + key.name]] != null)
- var item = (syntax[names[buffer[top + key.name]]])
- var deriv = buffer[top + key.deriv]
- if (typeof item != 'undefined') {
- assert(typeof item.derivations != 'undefined')
- if (deriv < item.derivations.length) {
- if (buffer[top + key.n] == item.derivations[deriv].length) {
- // pop
- buffer[parent + key.amount] += buffer[top + key.amount]
- buffer[parent + offset + buffer[parent + key.n]] = count
- buffer[parent + key.n] += 1
- var new_saved = N*count
- saved[new_saved + key.name] = buffer[top + key.name]
- saved[new_saved + key.amount] = buffer[top + key.amount]
- saved[new_saved + key.n] = buffer[top + key.n]
- var j = 0
- while(j < buffer[top + key.n]) {
- saved[new_saved + offset + j] = (
- buffer[top + offset + j]
- )
- j++
- }
- count += 1
- cur -= 1
- } else {
- buffer[top + key.n] = 0
- buffer[top + key.amount] = 0
- var d = item.derivations[deriv]
- if (d.length > 0) {
- var push_parent = cur
- var k = 0
- while (k < d.length) {
- // push(part: d[k], parent: push_parent)
- var new_top = top + N*(k+1)
- buffer[new_top + key.name] = (
- id_of_name[d[d.length-k-1]]
- )
- buffer[new_top + key.amount] = 0
- buffer[new_top + key.parent] = push_parent
- buffer[new_top + key.pos] = -1
- buffer[new_top + key.deriv] = 0
- buffer[new_top + key.n] = 0
- k++
- }
- cur += d.length
- } else {
- // pop
- buffer[parent + key.amount] += buffer[top + key.amount]
- buffer[parent + offset + buffer[parent + key.n]] = count
- buffer[parent + key.n] += 1
- var new_saved = N*count
- saved[new_saved + key.name] = buffer[top + key.name]
- saved[new_saved + key.amount] = buffer[top + key.amount]
- saved[new_saved + key.n] = buffer[top + key.n]
- var j = 0
- while(j < buffer[top + key.n]) {
- saved[new_saved + offset + j] = (
- buffer[top + offset + j]
- )
- j++
- }
- count += 1
- cur -= 1
- }
- }
- } else {
- // rollback
- buffer[parent + key.deriv] += 1
- cur = buffer[top + key.parent]
- }
- } else if ((names[buffer[top + key.name]])[0] == '~') {
- var kw_ok = false
- if (pos < tokens.length) {
- kw_ok = true
- var u = 1
- while (u < (names[buffer[top + key.name]]).length) {
- kw_ok = kw_ok && ((names[buffer[top + key.name]])[u] == tokens[pos].matched.string[u-1])
- if (!kw_ok) { break }
- u++
- }
- }
- if (kw_ok) {
- // pop token
- buffer[parent + key.amount] += 1
- buffer[parent + offset + buffer[parent + key.n]] = -pos
- buffer[parent + key.n] += 1
- cur -= 1
- } else {
- // rollback
- buffer[parent + key.deriv] += 1
- cur = buffer[top + key.parent]
- }
- } else if (pos < tokens.length && tokens[pos].matched.name == names[buffer[top + key.name]]) {
- // pop token
- buffer[parent + key.amount] += 1
- buffer[parent + offset + buffer[parent + key.n]] = -pos
- buffer[parent + key.n] += 1
- cur -= 1
- } else {
- // rollback
- buffer[parent + key.deriv] += 1
- cur = buffer[top + key.parent]
- }
- i++
- //console.log(i, cur, watch())
- }
-
- return { count: count, array: saved }
- }
- function match_part1 (syntax, tokens, part_name, pos) {
- let t_enter = performance.now()
- let output = []
- let stack = [{
- tree: {
- name: '[root]',
- children: [],
- amount: 0
- },
- pos: pos,
- parent: -1,
- deriv_index: 0
- }]
- let cur = 0
- let count = 0
- for (let i=0; i<10000; i++) {
- output.push([])
- }
- for (let i=0; i<1000; i++) {
- stack.push({
- tree: {
- name: null,
- children: [],
- amount: -1
- },
- pos: -1,
- parent: -1,
- deriv_index: -1
- })
- }
-
- function push(name, parent) {
- let top = stack[++cur]
- top.tree.name = name
- wipe_list(top.tree.children)
- top.tree.amount = 0
- top.parent = parent
- top.deriv_index = 0
- top.push_time = performance.now()
- }
-
- function pop() {
- let top = stack[cur]
- stack[top.parent].tree.amount += top.tree.amount
- stack[top.parent].tree.children.push(count)
-
- copy_list(output[count], top.tree.children)
- count++
- wipe_list(top.tree.children)
-
- /*
- stack[top.parent].tree.children.push({
- name: top.tree.name,
- amount: top.tree.amount,
- children: top.tree.children,
- time: (performance.now() - top.push_time)
- })
- */
- cur--
- }
-
- function rollback() {
- let top = stack[cur]
- stack[top.parent].deriv_index++
- cur = top.parent
- }
-
- push(part_name, 0)
-
- let i = 0
- let max = 0
-
- while (cur > 0) {
- let top = stack[cur]
- if (top.parent >= 0) {
- top.pos = stack[top.parent].pos + stack[top.parent].tree.amount
- }
- let token = (top.pos < tokens.length)? tokens[top.pos]: Token.Null
- let part = top.tree.name
- if (has(syntax, part)) {
- let item = syntax[part]
- if (typeof item == 'function') {
- let match = (item())(syntax, tokens, top.pos)
- if (match != null) {
- assert(false)
- top.tree.amount = match.amount
- top.tree.children = match.children
- pop()
- } else {
- rollback()
- }
- } else {
- let derivations = item.derivations
- if (top.deriv_index < derivations.length) {
- let built = top.tree.children.length
- let required = derivations[top.deriv_index].length
- if (built == required) {
- pop()
- } else {
- wipe_list(top.tree.children)
- top.tree.amount = 0
- let d = derivations[top.deriv_index]
- if (d.length > 0) {
- let parent = cur
- for (let part of rev(d)) {
- push(part, parent)
- }
- } else {
- pop()
- }
- }
- } else {
- rollback()
- }
- }
- } else if (part.startsWith('~')) {
- let keyword = part.slice(1, part.length)
- if (token != Token.Null && token.matched.string == keyword) {
- top.tree.name = 'Keyword'
- top.tree.amount = 1
- top.tree.children.push(token)
- pop()
- } else {
- rollback()
- }
- } else if (token != Token.Null && token.matched.name == part) {
- top.tree.amount = 1
- top.tree.children.push(token)
- pop()
- } else {
- rollback()
- }
- if (cur > max) { max = cur }
- i++
- //console.log(i++, cur, RawCompound(Clone(CookCompound(stack.slice(0,11)))))
- }
- console.log("max", max)
- console.log("loop", i)
- console.log("out-time", performance.now()-t_enter)
- /*
- let root = stack[0]
- if (root.tree.children.length > 0) {
- return root.tree.children[0]
- } else {
- return null
- }*/
- return { count: count, output: output }
- }
- // DFS
- function match_item (syntax, tokens, item_name, pos) {
- let item = syntax[item_name]
- if (is(item, Fun)) {
- let t0 = performance.now()
- let match = (item())(syntax, tokens, pos)
- let t1 = performance.now()
- if (match != null) {
- match.name = item_name
- match.time = t1-t0
- return match
- }
- } else if(has(item, 'derivations')) {
- for (let i=0; i<item.derivations.length; i++) {
- let d = item.derivations[i]
- let t0 = performance.now()
- let match = null
- let amount = 0
- let children = []
- let failed = false
- for (let i=0; i<d.length; i++) {
- let part = d[i]
- let p = pos + amount
- let token = (p < tokens.length)? tokens[p]: Token.Null
- let i_match = null
- if (has(syntax, part)) {
- i_match = match_item(syntax, tokens, part, p)
- } else if (part.startsWith('~')) {
- let keyword = part.slice(1, part.length)
- let valid = is(token, Token('Identifier'))
- if (valid && token.matched.string == keyword) {
- i_match = {
- amount: 1,
- name: 'Keyword',
- children: token
- }
- }
- } else {
- if (is(token, Token(part))) {
- i_match = {
- amount: 1,
- name: part, children: token
- }
- }
- }
- if (i_match != null) {
- amount += i_match.amount
- children.push(i_match)
- } else {
- failed = true
- break
- }
- }
- match = failed? null: { amount: amount, children: children }
- let t1 = performance.now()
- if (match != null) {
- match.name = item_name
- match.time = t1-t0
- return match
- }
- }
- return null
- } else {
- assert(false)
- }
- }
- function build_tree (syntax, root, tokens, pos = 0) {
- let match = match_part(syntax, PartList, tokens, root, pos)
- //assert(is(match, SyntaxTreeRoot))
- // TODO: check amount
- return match
- //return (match != null)? match: { ok: false, amount: 0 }
- }
- function parse_simple (syntax, tokens, pos) {
- /**
- * parse simple experssion using shunting yard algorithm
- *
- * 1. invoke converge() to parse argument list and key expression
- * 2. define operations on output & operator stack
- * 3. run the shunting yard algorithm
- * 4. get final state of stacks and return result
- */
- function* converge () {
- let offset = 0
- while (pos+offset < tokens.length) {
- let p = pos + offset
- let token = tokens[p]
- let left = (p-1 >= 0)? tokens[p-1]: Token.Null
- let is_args = ( is(token, Token('(')) && is(left, Token('Call')) )
- let is_key = ( is(token, Token('[')) && is(left, Token('Get')) )
- let is_par = ( is(token, Token('(')) && is_not(left, Token('Call')) )
- if ( is_args || is_key ) {
- let type = is_args? 'args': 'key'
- let syntax_item = (
- { args: 'ArgList', key: 'Key' }
- )[type]
- let err_msg = (
- { args: 'bad argument list', key: 'bad key' }
- )[type]
- let match = build_tree(syntax, syntax_item, tokens, p)
- let err = parser_error(token, err_msg)
- err.assert(match != null)
- yield match
- offset += match.amount
- } else if ( is_par ) {
- let syntax_item = 'Wrapped'
- let err_msg = 'missing )'
- let match = build_tree(syntax, syntax_item, tokens, p)
- let err = parser_error(token, err_msg)
- err.assert(match != null)
- yield match
- offset += match.amount
- } else {
- let left_is_operand = is(left, $u(SimpleOperand, Token(')')))
- let this_is_operand = is(token, $u(SimpleOperand, Token('(')))
- if ( left_is_operand && this_is_operand && offset > 0 ) {
- break
- } else {
- yield build_leaf(token)
- offset += 1
- }
- }
- }
- }
- let sentinel = {
- position: { row: -1, col: -1 },
- matched: { category: 'Operator', name: 'Sentinel', string: '' }
- }
- let input = cat(converge(), [{ name: 'Sentinel', children: sentinel }])
- let info = (operator => SimpleOperator[operator.matched.name])
- function empty (state) {
- let operators = state.operators
- return operators.length == 0
- }
- function top (state) {
- let operators = state.operators
- assert(operators.length > 0)
- return operators[operators.length-1]
- }
- function pop (state) {
- let operators = state.operators
- let output = state.output
- let operator = top(state)
- let err_op = parser_error(operator, 'missing operand')
- let type = info(operator).type
- let count = ({ prefix: 1, infix: 2 })[type]
- err_op.assert(output.length >= count)
- let take_out = output.slice(output.length-count, output.length)
- let remaining = output.slice(0, output.length-count)
- let poped = operators.slice(0, operators.length-1)
- let children = added_front(take_out, build_leaf(operator))
- let err_arg_msg = 'argument list involved in non-call expression'
- let err_arg = parser_error(operator, err_arg_msg)
- err_arg.if(
- is_not(operator, Token('Call'))
- && exists(children, operand => operand.name == 'ArgList')
- )
- let reduced = {
- name: 'SimpleUnit',
- children: children,
- amount: fold(children, 0, (tree, sum) => sum+tree.amount)
- }
- return {
- output: added(remaining, reduced),
- operators: poped
- }
- }
- function push (state, operator) {
- let operators = state.operators
- let output = state.output
- return {
- output: output,
- operators: added(operators, operator)
- }
- }
- function append (state, operand) {
- let operators = state.operators
- let output = state.output
- let element = is(operand, Token)? build_leaf(operand): operand
- return {
- output: added(output, element),
- operators: operators
- }
- }
- function put (state, operator) {
- let operators = state.operators
- let output = state.output
- return (empty(state))? push(state, operator): (function() {
- let input = operator
- let stack = top(state)
- let input_info = info(input)
- let stack_info = info(stack)
- let assoc = input_info.assoc
- let should_pop = ({
- left: input_info.priority <= stack_info.priority,
- right: input_info.priority < stack_info.priority
- })[assoc]
- return should_pop? put(pop(state), operator): push(state, input)
- })()
- }
- function get_result (state) {
- let operators = state.operators
- let output = state.output
- let output_first = output[0] || null
- let err = parser_error(output_first, 'missing operator')
- err.if(output.length > 1) // 1 = final result
- assert(output.length == 1 && operators.length == 1)
- return output_first
- }
- let initial = { output: [], operators: [] }
- let final = fold(input, initial, function (element, state) {
- // console.log(state)
- let TreeElement = $_(SyntaxTreeLeaf)
- let LeafElement = SyntaxTreeLeaf
- let add_to_output = (operand => append(state, operand))
- let put_operator = (operator => put(state, operator))
- let terminate = (t => BreakWith(put(state, sentinel)) )
- return transform(element, [
- { when_it_is: TreeElement, use: add_to_output },
- { when_it_is: LeafElement, use: l => transform(l.children, [
- { when_it_is: SimpleOperand, use: add_to_output },
- { when_it_is: SimpleOperator, use: put_operator },
- { when_it_is: Otherwise, use: terminate }
- ]) }
- ])
- })
- let EmptyState = Struct({
- output: $(x => x.length == 0),
- operators: $(x => x.length == 1)
- })
- return (is(final, EmptyState))? null: (function() {
- let result = get_result(final)
- return {
- amount: result.amount,
- name: 'Simple',
- children: [result]
- }
- })()
- }
- function parse (string) {
- console.log('parsing...')
- let tokens = get_tokens(string)
- let match = build_tree(Syntax, 'Module', tokens)
- let stuck_info = 'parser stuck: please check syntax'
- let stuck_err = parser_error(tokens[match.amount], stuck_info)
- stuck_err.if(match.amount < tokens.length)
- console.log('done')
- let tree = match
- return tree
- }
|