This commit is contained in:
Florian Nücke 2014-03-04 13:32:25 +01:00
commit b77585b66a

View File

@ -11,79 +11,200 @@ local term = require("term")
local text = require("text") local text = require("text")
local unicode = require("unicode") local unicode = require("unicode")
local function expandVars(token) function expandParam(param)
local name = nil local par, word, op = nil, nil, nil
local special = false for _, oper in ipairs{':%-', '%-', ':=', '=', ':%?','%?', ':%+', '%+'} do
local ignore = false par, word = param:match("(.-)"..oper.."(.*)")
local ignoreChar ='' if word then
op = oper
break
end
end
if word then
local stat = os.getenv(par)
if op == ':%-' then
if stat ~= '' and stat ~= nil then
return stat
else
return word
end
elseif op == '%-' then
if stat ~= '' and stat ~= nil then
return stat
elseif stat == '' then
return nil
elseif stat == nil then
return expand(word)
end
elseif op == ':=' then
if stat ~= '' and stat ~= nil then
return stat
else
os.setenv(par, word)
return expand(word)
end
elseif op == '=' then
if stat ~= '' and stat ~= nil then
return stat
elseif stat == '' then
return nil
elseif stat == nil then
os.setenv(par, word)
return expand(word)
end
elseif op == ':%?' then
if stat ~= '' and stat ~= nil then
return stat
else
error(par.." is not set!")
end
elseif op == '%?' then
if stat ~= '' and stat ~= nil then
return stat
elseif stat == '' then
return nil
elseif stat == nil then
error(par.." is not set")
end
elseif op == ':%+' then
if stat ~= '' and stat ~= nil then
return expand(word)
else
return nil
end
elseif op == '%+' then
if stat ~= nil then
return expand(word)
else
return nil
end
end
elseif string.sub(param, 1,1) == '#' then
return #(os.getenv(param:sub(2, -1)))
else
return os.getenv(param)
end
end
function expandCmd(cmd)
return cmd
end
function expandMath(expr)
local success, reason = load("return "..expr, os.getenv("SHELL"), 't', {})
if success then
return success()
else
return reason
end
end
function expand(token)
local expr = {}
local matchStack = {}
local escaped = false local escaped = false
local lastEnd = 1 local lastEnd = 1
local doubleQuote = false local doubleQuote = false
local singleQuote = false local singleQuote = false
local mathBoth
local endToken = {} local endToken = {}
for i = 1, unicode.len(token) do for i = 1, unicode.len(token) do
local char = unicode.sub(token, i, i) local char = unicode.sub(token, i, i)
if escaped then if escaped then
if name then if expr then
table.insert(name, char) table.insert(expr, char)
end end
escaped = false escaped = false
elseif char == '\\' then elseif char == '\\' then
escaped = not escaped escaped = not escaped
table.insert(endToken, unicode.sub(token, lastEnd, i-1)) table.insert(endToken, unicode.sub(token, lastEnd, i-1))
lastEnd = i+1 lastEnd = i+1
elseif char == '"' and not singleQuote then elseif char == matchStack[#matchStack] then
doubleQuote = not doubleQuote local match
table.insert(endToken, unicode.sub(token, lastEnd, i-1)) table.remove(matchStack)
lastEnd = i+1 if char == '}' then
elseif char == "'" and not doubleQuote then local param = table.concat(table.remove(expr))
singleQuote = not singleQuote match = expandParam(param)
table.insert(endToken, unicode.sub(token, lastEnd, i-1)) elseif char == ')' then
lastEnd = i+1 if expr[#expr].cmd then
elseif char == "$" and not doubleQuote and not singleQuote then local cmd = table.concat(table.remove(expr))
if name then match = expandCmd(cmd)
ignore = true elseif expr[#expr].math then
if not mathBoth then
mathBoth = i
elseif mathBoth == i - 1 then
local mth = table.concat(table.remove(expr))
match = expandMath(mth)
mathBoth = nil
else
return nil, "Unmatched )"
end
end
elseif char == '`' then
local cmd = table.concat(table.remove(expr))
match = expandCmd(cmd)
elseif char == "'" then
singleQuote = false
elseif char == '"' then
doubleQuote = false
end
if #expr > 0 then
table.insert(expr[#expr], match)
else else
name = {} table.insert(endToken, match)
end
lastEnd = i+1
elseif char == '"' and not singleQuote then
doubleQuote = true
if #expr <= 1 then
table.insert(endToken, unicode.sub(token, lastEnd, i-1)) table.insert(endToken, unicode.sub(token, lastEnd, i-1))
end end
elseif char == '{' and #name == 0 then table.insert(matchStack, '"')
if ignore and ignoreChar == '' then
ignoreChar = '}'
else
special = true
end
elseif char == '(' and ignoreChar == '' then
ignoreChar = ')'
elseif char == '`' and special then
ignore = true
ignoreChar = '`'
elseif char == '}' and not ignore and not doubleQuote and not singleQuote then
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
lastEnd = i+1 lastEnd = i+1
elseif char == "'" and not doubleQuote then
singleQuote = not singleQuote
if #expr <= 0 then
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
end
table.insert(matchStack, "'")
lastEnd = i+1
elseif char == "$" and not singleQuote then
table.insert(expr, {})
if #expr <= 0 then
table.insert(endToken, unicode.sub(token, lastEnd, i-1))
end
elseif char == '{' and #expr > 0 and #(expr[#expr]) == 0 then
table.insert(matchStack, '}')
if expr[#expr] == 0 then
expr[#expr].special = true
end
elseif char == '(' and #expr > 0 and #(expr[#expr]) == 0 then
table.insert(matchStack, ')')
if expr[#expr].cmd then
expr[#expr].cmd = false
expr[#expr].math = true
else
expr[#expr].cmd = true
end
elseif char == '`' then
table.insert(expr, {cmd = true})
table.insert(matchStack, '`')
elseif char == '"' and not singleQuote then elseif char == '"' and not singleQuote then
doubleQuote = not doubleQuote doubleQuote = not doubleQuote
elseif char == "'" and not doubleQuote then elseif char == "'" and not doubleQuote then
singleQuote = not singleQuote singleQuote = not singleQuote
elseif name and (char:match("[%a%d_]") or special) then elseif #expr > 0 and (char:match("[%a%d_]") or #matchStack > 0) then
if char:match("%d") and #name == 0 then table.insert(expr[#expr], char)
error "Identifiers can't start with a digit!" elseif #expr > 0 then -- We are done with gathering the name
end table.insert(endToken, os.getenv(table.concat(table.remove(expr))))
table.insert(name, char)
elseif char == ignoreChar and ignore then
ignore = false
ignoreChar = ''
elseif name then -- We are done with gathering the name
table.insert(endToken, os.getenv(table.concat(name)))
name = nil
lastEnd = i lastEnd = i
end end
end end
if name then while #expr > 0 do
table.insert(endToken, os.getenv(table.concat(name))) local xpr = table.remove(expr)
name = nil table.insert(expr[#expr] or endToken, os.getenv(table.concat(xpr)))
else end
if lastEnd >= #token then
table.insert(endToken, unicode.sub(token, lastEnd, -1)) table.insert(endToken, unicode.sub(token, lastEnd, -1))
end end
return table.concat(endToken) return table.concat(endToken)
@ -96,7 +217,7 @@ local function parseCommand(tokens)
-- Variable expansion for all command parts. -- Variable expansion for all command parts.
for i = 1, #tokens do for i = 1, #tokens do
tokens[i] = expandVars(tokens[i]) tokens[i] = expand(tokens[i])
end end
-- Resolve alias for command. -- Resolve alias for command.