local Pawn = {}
-- Pawn token types
local TokenType = {
KEYWORD = 1,
IDENTIFIER = 2,
NUMBER = 3,
STRING = 4,
OPERATOR = 5,
PREPROCESSOR = 6,
COMMENT = 7
}
-- Pawn keywords
local KEYWORDS = {
"new", "enum", "public", "stock", "static", "const", "forward",
"native", "if", "else", "for", "while", "do", "switch", "case",
"default", "return", "break", "continue", "sizeof", "state",
"goto", "tagof", "char", "bool", "int", "float", "void"
}
-- Preprocessor directives
local PREPROCESSOR_DIRECTIVES = {
"#define", "#include", "#if", "#else", "#endif", "#ifdef", "#ifndef",
"#pragma", "#error", "#warning"
}
function Pawn.tokenize(code)
local tokens = {}
local pos = 1
local len = #code
while pos <= len do
local char = code:sub(pos, pos)
-- Skip whitespace
if char:match("%s") then
pos = pos + 1
-- Comments
elseif char == "/" and code:sub(pos + 1, pos + 1) == "/" then
local start = pos
while pos <= len and code:sub(pos, pos) ~= "\n" do
pos = pos + 1
end
table.insert(tokens, {
type = TokenType.COMMENT,
value = code:sub(start, pos - 1)
})
-- Multi-line comments
elseif char == "/" and code:sub(pos + 1, pos + 1) == "*" then
local start = pos
pos = pos + 2
while pos <= len and not (code:sub(pos, pos) == "*" and code:sub(pos + 1, pos + 1) == "/") do
pos = pos + 1
end
pos = pos + 2
table.insert(tokens, {
type = TokenType.COMMENT,
value = code:sub(start, pos - 1)
})
-- Preprocessor directives
elseif char == "#" then
local start = pos
while pos <= len and not code:sub(pos, pos):match("%s") and code:sub(pos, pos) ~= "\n" do
pos = pos + 1
end
local directive = code:sub(start, pos - 1)
table.insert(tokens, {
type = TokenType.PREPROCESSOR,
value = directive
})
-- Strings
elseif char == '"' then
local start = pos
pos = pos + 1
while pos <= len and code:sub(pos, pos) ~= '"' do
if code:sub(pos, pos) == "\\" then
pos = pos + 1 -- Skip escape sequences
end
pos = pos + 1
end
pos = pos + 1
table.insert(tokens, {
type = TokenType.STRING,
value = code:sub(start, pos - 1)
})
-- Numbers
elseif char:match("%d") or (char == "." and code:sub(pos + 1, pos + 1):match("%d")) then
local start = pos
while pos <= len and (code:sub(pos, pos):match("%d") or code:sub(pos, pos) == "." or code:sub(pos, pos):match("[eE]") or (code:sub(pos, pos) == "-" and code:sub(pos - 1, pos - 1):match("[eE]"))) do
pos = pos + 1
end
table.insert(tokens, {
type = TokenType.NUMBER,
value = code:sub(start, pos - 1)
})
-- Identifiers and keywords
elseif char:match("[%a_]") then
local start = pos
while pos <= len and code:sub(pos, pos):match("[%w_]") do
pos = pos + 1
end
local identifier = code:sub(start, pos - 1)
local isKeyword = false
for _, kw in ipairs(KEYWORDS) do
if kw == identifier then
isKeyword = true
break
end
end
table.insert(tokens, {
type = isKeyword and TokenType.KEYWORD or TokenType.IDENTIFIER,
value = identifier
})
-- Operators
else
local operators = {"++", "--", "+=", "-=", "*=", "/=", "%=", "==", "!=", ">=", "<=", "&&", "||", ">>", "<<"}
local found = false
for _, op in ipairs(operators) do
if code:sub(pos, pos + #op - 1) == op then
table.insert(tokens, {
type = TokenType.OPERATOR,
value = op
})
pos = pos + #op
found = true
break
end
end
if not found then
table.insert(tokens, {
type = TokenType.OPERATOR,
value = char
})
pos = pos + 1
end
end
end
return tokens
end
function Pawn.transpileToLua(pawnCode)
local tokens = Pawn.tokenize(pawnCode)
local luaCode = ""
local i = 1
while i <= #tokens do
local token = tokens[i]
if token.type == TokenType.PREPROCESSOR then
if token.value == "#define" then
-- Handle #define MACRO value
local name = tokens[i + 1] and tokens[i + 1].value or ""
local value = tokens[i + 2] and tokens[i + 2].value or ""
if name:match("^[%a_][%w_]*$") then
luaCode = luaCode .. "local " .. name .. " = " .. value .. "\n"
i = i + 3
else
luaCode = luaCode .. "-- " .. token.value .. "\n"
i = i + 1
end
else
luaCode = luaCode .. "-- " .. token.value .. "\n"
i = i + 1
end
elseif token.type == TokenType.KEYWORD and token.value == "enum" then
-- Handle enum
i = i + 1 -- skip enum
local enumName = tokens[i].value
i = i + 2 -- skip name and {
local enumValues = {}
local currentValue = 0
while i <= #tokens and tokens[i].value ~= "}" do
if tokens[i].type == TokenType.IDENTIFIER then
local name = tokens[i].value
i = i + 1
if tokens[i] and tokens[i].value == "=" then
i = i + 1
currentValue = tonumber(tokens[i].value) or 0
i = i + 1
end
table.insert(enumValues, {name = name, value = currentValue})
currentValue = currentValue + 1
if tokens[i] and tokens[i].value == "," then
i = i + 1
end
else
i = i + 1
end
end
i = i + 1 -- skip }
-- Convert enum to Lua table
luaCode = luaCode .. "local " .. enumName .. " = {\n"
for _, ev in ipairs(enumValues) do
luaCode = luaCode .. " " .. ev.name .. " = " .. ev.value .. ",\n"
end
luaCode = luaCode .. "}\n"
elseif token.type == TokenType.KEYWORD and token.value == "new" then
-- Handle variable declaration
i = i + 1 -- skip new
local varName = tokens[i].value
i = i + 1
if tokens[i] and tokens[i].value == "[" then
-- Array declaration
i = i + 1
local size = tokens[i].value
i = i + 2 -- skip size and ]
luaCode = luaCode .. "local " .. varName .. " = {}\n"
luaCode = luaCode .. "for i = 1, " .. size .. " do\n"
luaCode = luaCode .. " " .. varName .. "[i] = 0\n"
luaCode = luaCode .. "end\n"
else
-- Single variable
luaCode = luaCode .. "local " .. varName .. " = 0\n"
end
else
-- Direct translation for other tokens
luaCode = luaCode .. token.value .. " "
i = i + 1
end
end
return luaCode
end
-- Example usage:
local pawnCode = [[
#define MAX_PLAYERS 500
enum pInfo {
PlayerName[25],
Kills,
Level,
Points
}
new PlayerInfo[MAX_PLAYERS][pInfo];
public OnPlayerConnect(playerid)
{
PlayerInfo[playerid][Kills] = 0;
PlayerInfo[playerid][Level] = 1;
return 1;
}
]]
local luaCode = Pawn.transpileToLua(pawnCode)
print("Transpiled Lua code:")
print(luaCode)