| ------------------------------------------------------------------------------- |
| -- Copyright (c) 2006-2013 Fabien Fleutot and others. |
| -- |
| -- All rights reserved. |
| -- |
| -- This program and the accompanying materials are made available |
| -- under the terms of the Eclipse Public License v1.0 which |
| -- accompanies this distribution, and is available at |
| -- http://www.eclipse.org/legal/epl-v10.html |
| -- |
| -- This program and the accompanying materials are also made available |
| -- under the terms of the MIT public license which accompanies this |
| -- distribution, and is available at http://www.lua.org/license.html |
| -- |
| -- Contributors: |
| -- Fabien Fleutot - API and implementation |
| -- |
| ------------------------------------------------------------------------------- |
| |
| ------------------------------------------------------------------------------- |
| -- |
| -- Exported API: |
| -- * [mlp.expr()] |
| -- * [mlp.expr_list()] |
| -- * [mlp.func_val()] |
| -- |
| ------------------------------------------------------------------------------- |
| |
| local gg = require 'metalua.grammar.generator' |
| local mlp = require 'metalua.compiler.parser.common' |
| local M = { } |
| |
| local mlp_table = require 'metalua.compiler.parser.table' |
| local mlp_meta = require 'metalua.compiler.parser.meta' |
| local mlp_misc = require 'metalua.compiler.parser.misc' |
| local annot = require 'metalua.compiler.parser.annot' |
| |
| -- Delayed dependencies toward externally-defined parsers |
| local function block (lx) return mlp.block (lx) end |
| local function stat (lx) return mlp.stat (lx) end |
| |
| -- For recursive definitions |
| local function expr (lx) return M.expr (lx) end |
| |
| local id = mlp_misc.id |
| |
| |
| -------------------------------------------------------------------------------- |
| -- Non-empty expression list. Actually, this isn't used here, but that's |
| -- handy to give to users. |
| -------------------------------------------------------------------------------- |
| M.expr_list = gg.list{ primary=expr, separators="," } |
| |
| -------------------------------------------------------------------------------- |
| -- Helpers for function applications / method applications |
| -------------------------------------------------------------------------------- |
| M.func_args_content = gg.list{ |
| name = "function arguments", |
| primary = expr, |
| separators = ",", |
| terminators = ")" } |
| |
| -- Used to parse methods |
| M.method_args = gg.multisequence{ |
| name = "function argument(s)", |
| { "{", mlp_table.content, "}" }, |
| { "(", M.func_args_content, ")", builder = unpack }, |
| { "+{", mlp_meta.quote_content, "}" }, |
| function(lx) local r = mlp.opt_string(lx); return r and {r} or { } end } |
| |
| -------------------------------------------------------------------------------- |
| -- [func_val] parses a function, from opening parameters parenthese to |
| -- "end" keyword included. Used for anonymous functions as well as |
| -- function declaration statements (both local and global). |
| -- |
| -- It's wrapped in a [_func_val] eta expansion, so that when expr |
| -- parser uses the latter, they will notice updates of [func_val] |
| -- definitions. |
| -------------------------------------------------------------------------------- |
| M.func_params_content = gg.list{ name="function parameters", |
| gg.multisequence{ { "...", builder = "Dots" }, annot.opt(id, 'te') }, |
| separators = ",", terminators = {")", "|"} } |
| |
| M.func_val = gg.sequence{ name="function body", |
| "(", M.func_params_content, ")", block, "end", |
| builder = function(x) |
| local params, body = unpack(x) |
| local annots, some = { }, false |
| for i, p in ipairs(params) do |
| if p.tag=='Annot' then |
| params[i], annots[i], some = p[1], p[2], true |
| else annots[i] = false end |
| end |
| if some then return { tag='Function', params, body, annots } |
| else return { tag='Function', params, body } end |
| end } |
| |
| local func_val = function(lx) return M.func_val(lx) end |
| |
| -------------------------------------------------------------------------------- |
| -- Default parser for primary expressions |
| -------------------------------------------------------------------------------- |
| function M.id_or_literal (lx) |
| local a = lx:next() |
| if a.tag~="Id" and a.tag~="String" and a.tag~="Number" then |
| local msg |
| if a.tag=='Eof' then |
| msg = "End of file reached when an expression was expected" |
| elseif a.tag=='Keyword' then |
| msg = "An expression was expected, and `"..a[1].. |
| "' can't start an expression" |
| else |
| msg = "Unexpected expr token " .. table.tostring (a, 'nohash') |
| end |
| return gg.parse_error (lx, msg) |
| end |
| return a |
| end |
| |
| |
| -------------------------------------------------------------------------------- |
| -- Builder generator for operators. Wouldn't be worth it if "|x|" notation |
| -- were allowed, but then lua 5.1 wouldn't compile it |
| -------------------------------------------------------------------------------- |
| |
| -- opf1 = |op| |_,a| `Op{ op, a } |
| local function opf1 (op) return |
| function (_,a) return { tag="Op", op, a } end end |
| |
| -- opf2 = |op| |a,_,b| `Op{ op, a, b } |
| local function opf2 (op) return |
| function (a,_,b) return { tag="Op", op, a, b } end end |
| |
| -- opf2r = |op| |a,_,b| `Op{ op, b, a } -- (args reversed) |
| local function opf2r (op) return |
| function (a,_,b) return { tag="Op", op, b, a } end end |
| |
| local function op_ne(a, _, b) |
| -- This version allows to remove the "ne" operator from the AST definition. |
| -- However, it doesn't always produce the exact same bytecode as Lua 5.1. |
| return { tag="Op", "not", |
| { tag="Op", "eq", a, b, lineinfo= { |
| first = a.lineinfo.first, last = b.lineinfo.last } } } |
| end |
| |
| |
| -------------------------------------------------------------------------------- |
| -- |
| -- complete expression |
| -- |
| -------------------------------------------------------------------------------- |
| |
| -- FIXME: set line number. In [expr] transformers probably |
| |
| M.expr = gg.expr { name = "expression", |
| |
| primary = gg.multisequence{ name="expr primary", |
| { "(", expr, ")", builder = "Paren" }, |
| { "function", func_val, builder = unpack }, |
| { "-{", mlp_meta.splice_content, "}", builder = unpack }, |
| { "+{", mlp_meta.quote_content, "}", builder = unpack }, |
| { "nil", builder = "Nil" }, |
| { "true", builder = "True" }, |
| { "false", builder = "False" }, |
| { "...", builder = "Dots" }, |
| mlp_table.table, |
| M.id_or_literal }, |
| |
| infix = { name="expr infix op", |
| { "+", prec = 60, builder = opf2 "add" }, |
| { "-", prec = 60, builder = opf2 "sub" }, |
| { "*", prec = 70, builder = opf2 "mul" }, |
| { "/", prec = 70, builder = opf2 "div" }, |
| { "%", prec = 70, builder = opf2 "mod" }, |
| { "^", prec = 90, builder = opf2 "pow", assoc = "right" }, |
| { "..", prec = 40, builder = opf2 "concat", assoc = "right" }, |
| { "==", prec = 30, builder = opf2 "eq" }, |
| { "~=", prec = 30, builder = op_ne }, |
| { "<", prec = 30, builder = opf2 "lt" }, |
| { "<=", prec = 30, builder = opf2 "le" }, |
| { ">", prec = 30, builder = opf2r "lt" }, |
| { ">=", prec = 30, builder = opf2r "le" }, |
| { "and",prec = 20, builder = opf2 "and" }, |
| { "or", prec = 10, builder = opf2 "or" } }, |
| |
| prefix = { name="expr prefix op", |
| { "not", prec = 80, builder = opf1 "not" }, |
| { "#", prec = 80, builder = opf1 "len" }, |
| { "-", prec = 80, builder = opf1 "unm" } }, |
| |
| suffix = { name="expr suffix op", |
| { "[", expr, "]", builder = function (tab, idx) |
| return {tag="Index", tab, idx[1]} end}, |
| { ".", id, builder = function (tab, field) |
| return {tag="Index", tab, mlp_misc.id2string(field[1])} end }, |
| { "(", M.func_args_content, ")", builder = function(f, args) |
| return {tag="Call", f, unpack(args[1])} end }, |
| { "{", mlp_table.content, "}", builder = function (f, arg) |
| return {tag="Call", f, arg[1]} end}, |
| { ":", id, M.method_args, builder = function (obj, post) |
| local m_name, args = unpack(post) |
| return {tag="Invoke", obj, mlp_misc.id2string(m_name), unpack(args)} end}, |
| { "+{", mlp_meta.quote_content, "}", builder = function (f, arg) |
| return {tag="Call", f, arg[1] } end }, |
| default = { name="opt_string_arg", parse = mlp_misc.opt_string, builder = function(f, arg) |
| return {tag="Call", f, arg } end } } } |
| |
| return M |