Generation of Lua code from AST
This commit is contained in:
parent
54d0f70640
commit
900f79732a
|
|
@ -210,7 +210,6 @@ func parse(tokens []Token) []Expr {
|
||||||
expressions: parse(blockTokens),
|
expressions: parse(blockTokens),
|
||||||
},
|
},
|
||||||
})
|
})
|
||||||
|
|
||||||
case "elseif":
|
case "elseif":
|
||||||
condTokens := getCondition()
|
condTokens := getCondition()
|
||||||
blockTokens := getBlock()
|
blockTokens := getBlock()
|
||||||
|
|
|
||||||
236
Script/main.luau
236
Script/main.luau
|
|
@ -10,7 +10,6 @@ type TokenKind =
|
||||||
| "NEWLINE"
|
| "NEWLINE"
|
||||||
| "IDENTIFIER"
|
| "IDENTIFIER"
|
||||||
| "NUMBER"
|
| "NUMBER"
|
||||||
| "COMMENT"
|
|
||||||
| "STRING"
|
| "STRING"
|
||||||
| "KEYWORD"
|
| "KEYWORD"
|
||||||
| "TEXTOPERATOR"
|
| "TEXTOPERATOR"
|
||||||
|
|
@ -25,14 +24,22 @@ type TokenKind =
|
||||||
| "DIVIDE"
|
| "DIVIDE"
|
||||||
| "MODULO"
|
| "MODULO"
|
||||||
|
|
||||||
|
type ExprKind =
|
||||||
|
"block"
|
||||||
|
| "if"
|
||||||
|
| "elseif"
|
||||||
|
| "else"
|
||||||
|
| "binop"
|
||||||
|
| "postfix"
|
||||||
|
| "functioncall"
|
||||||
|
| "identifier"
|
||||||
|
| "number"
|
||||||
|
| "string"
|
||||||
|
|
||||||
local keywords = {
|
local keywords = {
|
||||||
["if"] = true,
|
["if"] = true,
|
||||||
["elseif"] = true,
|
["elseif"] = true,
|
||||||
["else"] = true,
|
["else"] = true,
|
||||||
["loop"] = true,
|
|
||||||
["for"] = true,
|
|
||||||
["break"] = true,
|
|
||||||
["continue"] = true,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
local textOperators = {
|
local textOperators = {
|
||||||
|
|
@ -70,8 +77,8 @@ type Token = {
|
||||||
}
|
}
|
||||||
|
|
||||||
type Expr = {
|
type Expr = {
|
||||||
|
kind: ExprKind,
|
||||||
startToken: Token,
|
startToken: Token,
|
||||||
kind: string,
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type BlockExpr = Expr & {
|
type BlockExpr = Expr & {
|
||||||
|
|
@ -81,7 +88,7 @@ type BlockExpr = Expr & {
|
||||||
local function BlockExpr(startToken: Token, expressions: { Expr }): BlockExpr
|
local function BlockExpr(startToken: Token, expressions: { Expr }): BlockExpr
|
||||||
return {
|
return {
|
||||||
startToken = startToken,
|
startToken = startToken,
|
||||||
kind = "block",
|
kind = "block" :: ExprKind,
|
||||||
expressions = expressions,
|
expressions = expressions,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
@ -98,7 +105,7 @@ local function IfExpr(
|
||||||
): IfExpr
|
): IfExpr
|
||||||
return {
|
return {
|
||||||
startToken = startToken,
|
startToken = startToken,
|
||||||
kind = "if",
|
kind = "if" :: ExprKind,
|
||||||
condition = condition,
|
condition = condition,
|
||||||
block = block,
|
block = block,
|
||||||
}
|
}
|
||||||
|
|
@ -116,7 +123,7 @@ local function ElseIfExpr(
|
||||||
): ElseIfExpr
|
): ElseIfExpr
|
||||||
return {
|
return {
|
||||||
startToken = startToken,
|
startToken = startToken,
|
||||||
kind = "elseif",
|
kind = "elseif" :: ExprKind,
|
||||||
condition = condition,
|
condition = condition,
|
||||||
block = block,
|
block = block,
|
||||||
}
|
}
|
||||||
|
|
@ -129,19 +136,7 @@ type ElseExpr = Expr & {
|
||||||
local function ElseExpr(startToken: Token, block: BlockExpr): ElseExpr
|
local function ElseExpr(startToken: Token, block: BlockExpr): ElseExpr
|
||||||
return {
|
return {
|
||||||
startToken = startToken,
|
startToken = startToken,
|
||||||
kind = "else",
|
kind = "else" :: ExprKind,
|
||||||
block = block,
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
type LoopExpr = Expr & {
|
|
||||||
block: Expr,
|
|
||||||
}
|
|
||||||
|
|
||||||
local function LoopExpr(startToken: Token, block: BlockExpr): LoopExpr
|
|
||||||
return {
|
|
||||||
startToken = startToken,
|
|
||||||
kind = "loop",
|
|
||||||
block = block,
|
block = block,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
@ -160,7 +155,7 @@ local function BinOpExpr(
|
||||||
): BinOpExpr
|
): BinOpExpr
|
||||||
return {
|
return {
|
||||||
startToken = startToken,
|
startToken = startToken,
|
||||||
kind = "binop",
|
kind = "binop" :: ExprKind,
|
||||||
left = left,
|
left = left,
|
||||||
right = right,
|
right = right,
|
||||||
operator = operator,
|
operator = operator,
|
||||||
|
|
@ -172,10 +167,14 @@ type PostfixOpExpr = Expr & {
|
||||||
operator: Token,
|
operator: Token,
|
||||||
}
|
}
|
||||||
|
|
||||||
local function PostfixOpExpr(startToken: Token, expr: Expr, operator: Token)
|
local function PostfixOpExpr(
|
||||||
|
startToken: Token,
|
||||||
|
expr: Expr,
|
||||||
|
operator: Token
|
||||||
|
): PostfixOpExpr
|
||||||
return {
|
return {
|
||||||
startToken = startToken,
|
startToken = startToken,
|
||||||
kind = "postfix",
|
kind = "postfix" :: ExprKind,
|
||||||
expr = expr,
|
expr = expr,
|
||||||
operator = operator,
|
operator = operator,
|
||||||
}
|
}
|
||||||
|
|
@ -193,7 +192,7 @@ local function FunctionCallExpr(
|
||||||
): FunctionCallExpr
|
): FunctionCallExpr
|
||||||
return {
|
return {
|
||||||
startToken = startToken,
|
startToken = startToken,
|
||||||
kind = "functioncall",
|
kind = "functioncall" :: ExprKind,
|
||||||
name = name,
|
name = name,
|
||||||
arg = arg,
|
arg = arg,
|
||||||
}
|
}
|
||||||
|
|
@ -207,7 +206,7 @@ local function IdentifierExpr(startToken: Token): IdentifierExpr
|
||||||
end
|
end
|
||||||
return {
|
return {
|
||||||
startToken = startToken,
|
startToken = startToken,
|
||||||
kind = "identifier",
|
kind = "identifier" :: ExprKind,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -219,7 +218,7 @@ local function NumberExpr(startToken: Token): NumberExpr
|
||||||
end
|
end
|
||||||
return {
|
return {
|
||||||
startToken = startToken,
|
startToken = startToken,
|
||||||
kind = "identifier",
|
kind = "number" :: ExprKind,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
@ -231,12 +230,113 @@ local function StringExpr(startToken: Token): StringExpr
|
||||||
end
|
end
|
||||||
return {
|
return {
|
||||||
startToken = startToken,
|
startToken = startToken,
|
||||||
kind = "identifier",
|
kind = "string" :: ExprKind,
|
||||||
}
|
}
|
||||||
end
|
end
|
||||||
|
|
||||||
-- yea
|
-- yea
|
||||||
|
|
||||||
|
local function generate(program: { Expr }): string
|
||||||
|
local output = ""
|
||||||
|
|
||||||
|
local i = 0
|
||||||
|
local len = #program
|
||||||
|
while i < len do
|
||||||
|
i += 1
|
||||||
|
local expr = program[i]
|
||||||
|
|
||||||
|
local kind = expr.kind
|
||||||
|
if kind == "binop" then
|
||||||
|
local binop = expr :: BinOpExpr
|
||||||
|
local value = binop.operator.value
|
||||||
|
|
||||||
|
if value == "=" then
|
||||||
|
output ..= "local "
|
||||||
|
end
|
||||||
|
|
||||||
|
output ..= generate { binop.left }
|
||||||
|
output ..= if value == "is" then " == " else ` {value} `
|
||||||
|
output ..= generate { binop.right }
|
||||||
|
output ..= "\n"
|
||||||
|
elseif kind == "identifier" then
|
||||||
|
local identifier = expr :: IdentifierExpr
|
||||||
|
output ..= identifier.startToken.value
|
||||||
|
elseif kind == "number" then
|
||||||
|
local number = expr :: NumberExpr
|
||||||
|
output ..= number.startToken.value
|
||||||
|
elseif kind == "string" then
|
||||||
|
local string = expr :: StringExpr
|
||||||
|
output ..= `"{string.startToken.value}"`
|
||||||
|
elseif kind == "functioncall" then
|
||||||
|
local functioncall = expr :: FunctionCallExpr
|
||||||
|
output ..= functioncall.name.value
|
||||||
|
output ..= "("
|
||||||
|
output ..= generate { functioncall.arg }
|
||||||
|
output ..= ");\n"
|
||||||
|
elseif kind == "if" then
|
||||||
|
local ifexpr = expr :: IfExpr
|
||||||
|
output ..= "\n(function() if "
|
||||||
|
output ..= generate { ifexpr.condition }
|
||||||
|
output ..= " then\n"
|
||||||
|
output ..= generate { ifexpr.block }
|
||||||
|
|
||||||
|
if i + 1 < len then
|
||||||
|
local nextExprKind = program[i + 1].kind
|
||||||
|
|
||||||
|
if nextExprKind ~= "elseif" and nextExprKind ~= "else" then
|
||||||
|
output ..= "end end)()"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif kind == "elseif" then
|
||||||
|
local elseifexpr = expr :: ElseIfExpr
|
||||||
|
output ..= "elseif "
|
||||||
|
output ..= generate { elseifexpr.condition }
|
||||||
|
output ..= " then\n"
|
||||||
|
output ..= generate { elseifexpr.block }
|
||||||
|
|
||||||
|
if i + 1 < len then
|
||||||
|
local nextExprKind = program[i + 1].kind
|
||||||
|
|
||||||
|
if nextExprKind ~= "else" then
|
||||||
|
output ..= "end end)()"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
elseif kind == "else" then
|
||||||
|
local elseexpr = expr :: ElseExpr
|
||||||
|
output ..= "else\n"
|
||||||
|
output ..= generate { elseexpr.block }
|
||||||
|
output ..= "end end)()\n"
|
||||||
|
elseif kind == "block" then
|
||||||
|
local block = expr :: BlockExpr
|
||||||
|
local b = 0
|
||||||
|
while b < #block.expressions - 1 do
|
||||||
|
b += 1
|
||||||
|
output ..= "\t" .. generate { block.expressions[b] }
|
||||||
|
end
|
||||||
|
output ..= "return "
|
||||||
|
output ..= "\t" .. generate { block.expressions[b + 1] }
|
||||||
|
output ..= "\n"
|
||||||
|
elseif kind == "postfix" then
|
||||||
|
local postfix = expr :: PostfixOpExpr
|
||||||
|
output ..= generate { postfix.expr }
|
||||||
|
|
||||||
|
local value = postfix.operator.value
|
||||||
|
|
||||||
|
if value == "++" then
|
||||||
|
output ..= " += 1\n"
|
||||||
|
elseif value == "--" then
|
||||||
|
output ..= " -= 1\n"
|
||||||
|
else
|
||||||
|
error(`unknown postfix operator {value}`)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
error(`unknown expr kind {kind}`)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
return output
|
||||||
|
end
|
||||||
|
|
||||||
local function parse(tokens: { Token }): { Expr }
|
local function parse(tokens: { Token }): { Expr }
|
||||||
local program: { Expr } = {}
|
local program: { Expr } = {}
|
||||||
|
|
||||||
|
|
@ -259,7 +359,7 @@ local function parse(tokens: { Token }): { Expr }
|
||||||
-- skip newline at start
|
-- skip newline at start
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
while i < len do
|
while i < len + 1 do -- todo figure out if the + 1 breaks something or not its 5:57 am idck
|
||||||
if tokens[i].kind == "NEWLINE" then
|
if tokens[i].kind == "NEWLINE" then
|
||||||
blockIndent = 0
|
blockIndent = 0
|
||||||
-- chock next few tokens to see if they're indented
|
-- chock next few tokens to see if they're indented
|
||||||
|
|
@ -342,12 +442,11 @@ local function parse(tokens: { Token }): { Expr }
|
||||||
-- skip newline
|
-- skip newline
|
||||||
i += 1
|
i += 1
|
||||||
|
|
||||||
addExpr(ElseExpr(token, BlockExpr(token, parse(getBlock()))))
|
local block = getBlock()
|
||||||
elseif token.value == "loop" then
|
|
||||||
-- skip newline
|
|
||||||
i += 1
|
|
||||||
|
|
||||||
addExpr(LoopExpr(token, BlockExpr(token, parse(getBlock()))))
|
print("else block", block)
|
||||||
|
|
||||||
|
addExpr(ElseExpr(token, BlockExpr(token, parse(block))))
|
||||||
else
|
else
|
||||||
print(token)
|
print(token)
|
||||||
error(colour.red "unknown token value " .. token.value)
|
error(colour.red "unknown token value " .. token.value)
|
||||||
|
|
@ -380,7 +479,8 @@ local function parse(tokens: { Token }): { Expr }
|
||||||
))
|
))
|
||||||
elseif postfixOperators[nextToken.value] then
|
elseif postfixOperators[nextToken.value] then
|
||||||
-- postfix
|
-- postfix
|
||||||
error "unimplemented"
|
i = advance
|
||||||
|
addExpr(PostfixOpExpr(token, IdentifierExpr(token), nextToken))
|
||||||
else
|
else
|
||||||
i -= 1 -- getCond skips the identifier
|
i -= 1 -- getCond skips the identifier
|
||||||
addExpr(FunctionCallExpr(token, token, parseCond(getCond())))
|
addExpr(FunctionCallExpr(token, token, parseCond(getCond())))
|
||||||
|
|
@ -399,9 +499,9 @@ local function parse(tokens: { Token }): { Expr }
|
||||||
addExpr(NumberExpr(token))
|
addExpr(NumberExpr(token))
|
||||||
end
|
end
|
||||||
|
|
||||||
if not nextToken then
|
if not nextToken or not binaryOperators[nextToken.value] then
|
||||||
standalone()
|
standalone()
|
||||||
elseif binaryOperators[nextToken.value] then
|
else
|
||||||
-- binop
|
-- binop
|
||||||
i = advance
|
i = advance
|
||||||
addExpr(BinOpExpr(
|
addExpr(BinOpExpr(
|
||||||
|
|
@ -411,8 +511,6 @@ local function parse(tokens: { Token }): { Expr }
|
||||||
parseCond(getCond()),
|
parseCond(getCond()),
|
||||||
nextToken
|
nextToken
|
||||||
))
|
))
|
||||||
else
|
|
||||||
standalone()
|
|
||||||
end
|
end
|
||||||
elseif token.kind == "STRING" then
|
elseif token.kind == "STRING" then
|
||||||
-- string is at the start of an expression, it could be:
|
-- string is at the start of an expression, it could be:
|
||||||
|
|
@ -428,9 +526,9 @@ local function parse(tokens: { Token }): { Expr }
|
||||||
addExpr(StringExpr(token))
|
addExpr(StringExpr(token))
|
||||||
end
|
end
|
||||||
|
|
||||||
if not nextToken then
|
if not nextToken or not binaryOperators[nextToken.value] then
|
||||||
standalone()
|
standalone()
|
||||||
elseif binaryOperators[nextToken.value] then
|
else
|
||||||
-- binop
|
-- binop
|
||||||
i = advance
|
i = advance
|
||||||
addExpr(BinOpExpr(
|
addExpr(BinOpExpr(
|
||||||
|
|
@ -440,12 +538,8 @@ local function parse(tokens: { Token }): { Expr }
|
||||||
parseCond(getCond()),
|
parseCond(getCond()),
|
||||||
nextToken
|
nextToken
|
||||||
))
|
))
|
||||||
else
|
|
||||||
standalone()
|
|
||||||
end
|
end
|
||||||
elseif token.kind == "SPACE" or token.kind == "COMMENT" then
|
elseif token.kind ~= "SPACE" then
|
||||||
-- wtf
|
|
||||||
else
|
|
||||||
print(token)
|
print(token)
|
||||||
error(colour.red "unknown token kind " .. token.kind)
|
error(colour.red "unknown token kind " .. token.kind)
|
||||||
end
|
end
|
||||||
|
|
@ -502,17 +596,15 @@ local function lex(source: { string }): { Token }
|
||||||
end
|
end
|
||||||
elseif char == ";" then
|
elseif char == ";" then
|
||||||
-- parse till end of line
|
-- parse till end of line
|
||||||
local startColumn = column
|
|
||||||
i += 1 -- skip the semicolon
|
i += 1 -- skip the semicolon
|
||||||
local comment = ""
|
|
||||||
while i < len and source[i] ~= "\n" do
|
while i < len and source[i] ~= "\n" do
|
||||||
comment ..= source[i]
|
|
||||||
column += 1
|
column += 1
|
||||||
i += 1
|
i += 1
|
||||||
end
|
end
|
||||||
column -= 1
|
column -= 1
|
||||||
i -= 1
|
i -= 1
|
||||||
addToken("COMMENT", comment, line, startColumn)
|
|
||||||
|
-- I used to do something with it here but nah
|
||||||
elseif char == '"' then
|
elseif char == '"' then
|
||||||
local startLine, startColumn = line, column
|
local startLine, startColumn = line, column
|
||||||
|
|
||||||
|
|
@ -564,7 +656,7 @@ local function lex(source: { string }): { Token }
|
||||||
addToken("DIVIDE", "/")
|
addToken("DIVIDE", "/")
|
||||||
elseif char == "%" then
|
elseif char == "%" then
|
||||||
addToken("MODULO", "%")
|
addToken("MODULO", "%")
|
||||||
else
|
elseif char ~= " " then
|
||||||
if char >= "0" and char <= "9" then
|
if char >= "0" and char <= "9" then
|
||||||
local startLine, startColumn = line, column
|
local startLine, startColumn = line, column
|
||||||
|
|
||||||
|
|
@ -600,12 +692,6 @@ local function lex(source: { string }): { Token }
|
||||||
i += 1
|
i += 1
|
||||||
end
|
end
|
||||||
|
|
||||||
if i == len then
|
|
||||||
-- you can't end a program with an identifier
|
|
||||||
print(colour.red "cant end program with identifier")
|
|
||||||
exit(1)
|
|
||||||
end
|
|
||||||
|
|
||||||
column -= 1
|
column -= 1
|
||||||
i -= 1
|
i -= 1
|
||||||
|
|
||||||
|
|
@ -682,39 +768,11 @@ local function main()
|
||||||
-- remove trailing newlines
|
-- remove trailing newlines
|
||||||
source = string.gsub(source, "\n+$", "")
|
source = string.gsub(source, "\n+$", "")
|
||||||
|
|
||||||
local split = string.split(source, "")
|
local tokens = lex(string.split(source, ""))
|
||||||
|
|
||||||
local tokens = lex(split)
|
|
||||||
|
|
||||||
for _, token in tokens do
|
|
||||||
if token.kind == "SPACE" then
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
if token.kind == "NEWLINE" then
|
|
||||||
print "────────────────┼───────────────┼─────────────────────────────"
|
|
||||||
continue
|
|
||||||
end
|
|
||||||
|
|
||||||
-- print in a nice format
|
|
||||||
local function pad(str: string, len: number): string
|
|
||||||
return str .. string.rep(" ", len - #str)
|
|
||||||
end
|
|
||||||
|
|
||||||
print(
|
|
||||||
pad(`{target}:{token.line}:{token.column}`, 15),
|
|
||||||
"│",
|
|
||||||
pad(colour.yellow(token.kind), 22),
|
|
||||||
"│",
|
|
||||||
colour.purple(token.value)
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
local program = parse(tokens)
|
local program = parse(tokens)
|
||||||
-- local out = generate(program)
|
local out = generate(program)
|
||||||
|
|
||||||
print(program)
|
print(out)
|
||||||
|
|
||||||
-- print(out)
|
|
||||||
end
|
end
|
||||||
|
|
||||||
main()
|
main()
|
||||||
|
|
|
||||||
|
|
@ -11,15 +11,8 @@ elseif x
|
||||||
else
|
else
|
||||||
print "x is falsy"
|
print "x is falsy"
|
||||||
|
|
||||||
loop
|
x++
|
||||||
print "infinite loop"
|
print "x is now {x}"
|
||||||
print "just kidding"
|
|
||||||
break
|
|
||||||
|
|
||||||
for 5
|
|
||||||
x++
|
|
||||||
print "this prints 5 times"
|
|
||||||
print "x is now {x}"
|
|
||||||
|
|
||||||
y = if x is 6
|
y = if x is 6
|
||||||
"yes"
|
"yes"
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue