local pprint = require "pprint" local program = [[ = = @rpn \\ [3] [4] dup * swap dup * swap + sqrt print = @rpn dup . @rpn.data $ = @rpn.data \\ $ $ = @rpn swap . @rpn.data \\ $x $y = @rpn.data \\ $y $x = @rpn * . @rpn.data \\ $y $x = @rpn.data $z . @code [$z = $x * $y] = @rpn + . @rpn.data \\ $y $x = @rpn.data $z . @code [$z = $x + $y] = @rpn sqrt . @rpn.data $x = @rpn.data $y . @code [$y = math.sqrt(tonumber($x))] = @rpn print . @rpn.data $x = @code [print($x)] = @rpn $x = @rpn.data $x ]] -- [TODO] make parser module -- - add symbol parsing hooks (for `[` and `"`) -- - add symbol stream hooks (for things like `( a b c )`) -- - add rule steam hooks (for things like `?`) -- - add post-parsing hooks (for full rule set transformation) local stream = program:gmatch(".") local char, rule, rule_symbol, label_symbol, depth, symbol, side, striding local tuple, rules, LEFT, RIGHT = {}, {}, 1, 2 repeat rule_symbol, char = "", stream() side, rule = LEFT, {{}, {}} while char and char:match("[%s%.,]") do -- skip all characters that can't be bound char = stream() end if char == nil then break elseif char == "[" then -- rule delimiter is multibyte depth, char = 1, stream() while char do if char == "[" then depth = depth + 1 end if char == "]" then depth = depth - 1 end if depth == 0 then break end rule_symbol, char = rule_symbol .. char, stream() end else -- rule delimiter is a single character rule_symbol = "" while char and not char:match("%s") do rule_symbol, char = rule_symbol .. char, stream() if char == "[" then io.stderr:write "Warning: symbol contains `[`. A space may be missing?" end end end repeat -- consume rules from this set char = stream() while char and (char:match("[%s]") or not label_symbol and char:match("[%.,]")) do -- skip whitespace or [.,] if a label hasn't been selected char = stream() end if not char then if label_symbol and not striding then table.insert(rule[side], {label_symbol, tuple}) end break end -- read a symbol from the stream if char:match("[%.,]") then -- we've hit a tuple related delimiter symbol = char elseif char == "[" then -- parse a boxed symbol symbol, depth, char = "", 1, stream() while char do if char == "[" then depth = depth + 1 end if char == "]" then depth = depth - 1 end if depth == 0 then break end symbol, char = symbol .. char, stream() end else -- parse a symbol seperated by spaces -- this segment warns if a symbol contains `[` symbol = "" while char and not char:match("%s") do symbol, char = symbol .. char, stream() if char == "[" then io.stderr:write "Warning: symbol contains `[`. A space may be missing?" end end end -- process that symbol if symbol == "," then -- push a tuple table.insert(rule[side], {label_symbol, tuple}) striding, tuple = false, {} elseif symbol == "." then -- push a tuple, clear label symbol if not (striding and #tuple == 0) then table.insert(rule[side], {label_symbol, tuple}) end striding, tuple, label_symbol = false, {}, nil elseif symbol == [[\\]] then if #tuple ~= 0 then table.insert(rule[side], {label_symbol, tuple}) tuple = {} end striding = true elseif symbol == rule_symbol and side == RIGHT then if label_symbol and not (striding and #tuple == 0) then table.insert(rule[side], {label_symbol, tuple}) tuple = {} end if #rule[LEFT] == 0 and #rule[RIGHT] == 0 then break else table.insert(rules, rule) striding, side, rule, label_symbol = false, LEFT, {{}, {}}, nil end elseif symbol == rule_symbol and side == LEFT then -- a rule dilimiter if label_symbol and not (striding and #tuple == 0) then table.insert(rule[side], {label_symbol, tuple}) end striding, tuple, side, label_symbol = false, {}, RIGHT, nil elseif not label_symbol then -- a label s symbol label_symbol = symbol elseif striding then table.insert(rule[side], {label_symbol, {symbol}}) else -- a symbol in a tuple table.insert(tuple, symbol) end until not char if #rule[LEFT] ~= 0 or #rule[RIGHT] ~= 0 then table.insert(rules, rule) end until not char local compile = require "compiler" print(compile(rules, { mode = "main", state_every_rewrite = false, state_at_start = true, state_at_end = true, }))