tjdevries / vim9jit

a vim9script -> lua transpiler (written in Rust)
MIT License
510 stars 21 forks source link

thoughts while away from comp #3

Closed tjdevries closed 1 year ago

tjdevries commented 3 years ago
local p = require('vim9jit.patterns')
local g = require('vim9jit.grammar')

local grammar_unpack = function(t)
  local results = {}
  for _, v in ipairs(t) do
    if type(v) == "string" then
      table.insert(results, p.literal(v))
    else
      table.insert(results, v)
    end
  end

  return unpack(results)
end

local literal = p.literal
local branch = function(t)
  return p.branch(grammar_unpack(t))
end

local any_amount = function(t)
  assert(#t == 1)
  if t.capture == false then
    return p.any_amount(t[1])
  else
    return p.capture(p.any_amount(t[1]))
  end
end

local any_amount_of = function(t)
  return p.any_amount(p.branch(grammar_unpack(t)))
end

local _wrap = function(generator)
  return function(t)
    local res = generator(grammar_unpack(t))
    if not t.skip then res = p.capture(res) end
    return res
  end
end

local any = _wrap(p.branch)
local seq = _wrap(p.concat)

local c_seq = function(t)
  return p.capture(p.concat(grammar_unpack(t)))
end

local c_any = function(t)
  return p.capture(p.branch(grammar_unpack(t)))
end

local seq = function(t)
  return p.concat(grammar_unpack(t))
end

local underscore = p.literal("_")
local digit = p.range('0', '9')
local letter = p.branch(
  p.range('a', 'z'),
  p.range('A', 'Z')
)

local _whitespace_table = {
  ' ',
  '\t',
  '\v',
  '\f'
}

local whitespace = p.set(grammar_unpack(_whitespace_table))
local whitespace_and_eol = p.set('\n', grammar_unpack(_whitespace_table))

local some_whitespace = p.one_or_more(whitespace)
local any_whitespace = p.any_amount(whitespace)
local any_whitespace_or_eol = p.any_amount(whitespace_and_eol)

local c_seqw = function(sequence)
  local spaces = any_whitespace
  if sequence.eol then
    spaces = any_whitespace_or_eol
  end

  local sequence_with_whitespace = {spaces}
  for i, v in ipairs(sequence) do
    table.insert(sequence_with_whitespace, v)

    -- Don't allow newlines until we say so explicitly
    if i == #sequence then
      table.insert(sequence_with_whitespace, any_whitespace)
    else
      table.insert(sequence_with_whitespace, spaces)
    end
  end
  return p.capture(p.concat(unpack(sequence_with_whitespace)))
end

local EOL = p.end_of_line
local EOL_or_EOF = p.branch(EOL, p.end_of_file)

local group = setmetatable({}, {
  __index = function(_, k)
    return p.group(k)
  end,
})

local make_grammar = function(root)
  return g.grammar {
    root or "vim9script",

    vim9script = c_seq {
      "vim9script", EOL_or_EOF,
      any_amount {
        branch {
          seq { group.Assign, EOL_or_EOF },
          EOF,
        },

        capture = false,
      },
    },

    Assign = c_seqw {
      group.VarName, "=", group.Expression,

      eol = true,
    },

    VarName = c_seq {
      branch { 
        letter, underscore
      },
      any_amount_of {
        letter, underscore, digit
      }
    },

    Expression = c_any {
      group.VarName,
    },

    Number = c_any {
      -- Hexadecimal
      c_seq {
        any { "0x", "0X", skip = true},
        p.one_or_more(p.branch(
          digit, p.range('a', 'f'), p.range('A', 'F')
        ))     
      },

      -- Float
      c_seq {
        p.one_or_more(digit), '.', p.one_or_more(digit),
      },

      -- Integer
      p.capture(p.one_or_more(digit)),
    },

  }
end

return {
  make_grammar = make_grammar
}

Some new thoughts