Skip to content

Commit

Permalink
Merge pull request #33 from titan-lang/globalvars
Browse files Browse the repository at this point in the history
Implementing module (top-level) variables
  • Loading branch information
mascarenhas committed Oct 21, 2017
2 parents 0ec8521 + eedd59a commit da8890a
Show file tree
Hide file tree
Showing 4 changed files with 490 additions and 67 deletions.
38 changes: 38 additions & 0 deletions examples/artisanal.titan
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,44 @@ function foo(x: integer, y: integer): integer
return x + y
end

function doublea(): integer
a = a * 2
return a
end

function doubleb(): integer
b = b * 2
return b
end

function setc(t: { integer }): nil
c = t
end

function sumc(): integer
local s = 0
for i = 1, #c do
s = s + c[i]
end
return s
end

function sumd(): integer
local s = 0
for i = 1, #d do
s = s + d[i]
end
return s
end

local a: integer = foo(2,3)

b: integer = foo(10,5)

local c: {integer} = {1,2,3}

d: {integer} = {3,4,5}

function filltable(N: integer): { integer }
local xs: { integer } = {}
for i=1,N do
Expand Down
167 changes: 165 additions & 2 deletions spec/coder_spec.lua
Original file line number Diff line number Diff line change
Expand Up @@ -327,12 +327,92 @@ describe("Titan code generator ", function()
assert.truthy(ok, err)
end)

pending("handles coercion to integer", function()
it("generates code for integer module-local variables", function()
local code = [[
local a: integer = 1
function geta(): integer
return a
end
function seta(x: integer): nil
a = x
end
]]
local ast, err = parser.parse(code)
assert.truthy(ast, err)
local ok, err = checker.check(ast, code, "test.titan")
assert.truthy(ok, err)
local ok, err = generate(ast, "titan_test")
assert.truthy(ok, err)
local ok, err = call("titan_test", "assert(titan_test.geta() == 1);titan_test.seta(2);assert(titan_test.geta() == 2)")
assert.truthy(ok, err)
end)

it("generates code for float module-local variables", function()
local code = [[
local a: float = 1
function geta(): float
return a
end
function seta(x: float): nil
a = x
end
]]
local ast, err = parser.parse(code)
assert.truthy(ast, err)
local ok, err = checker.check(ast, code, "test.titan")
assert.truthy(ok, err)
local ok, err = generate(ast, "titan_test")
assert.truthy(ok, err)
local ok, err = call("titan_test", "assert(titan_test.geta() == 1);titan_test.seta(2);assert(titan_test.geta() == 2)")
assert.truthy(ok, err)
end)

it("generates code for boolean module-local variables", function()
local code = [[
local a: boolean = true
function geta(): boolean
return a
end
function seta(x: boolean): nil
a = x
end
]]
local ast, err = parser.parse(code)
assert.truthy(ast, err)
local ok, err = checker.check(ast, code, "test.titan")
assert.truthy(ok, err)
local ok, err = generate(ast, "titan_test")
assert.truthy(ok, err)
local ok, err = call("titan_test", "assert(titan_test.geta() == true);titan_test.seta(false);assert(titan_test.geta() == false)")
assert.truthy(ok, err)
end)

it("generates code for array module-local variables", function()
local code = [[
local a: {integer} = {}
function len(): integer
return #a
end
function seta(x: {integer}): nil
a = x
end
]]
local ast, err = parser.parse(code)
assert.truthy(ast, err)
local ok, err = checker.check(ast, code, "test.titan")
assert.truthy(ok, err)
local ok, err = generate(ast, "titan_test")
assert.truthy(ok, err)
local ok, err = call("titan_test", "assert(titan_test.len() == 0);titan_test.seta({1});assert(titan_test.len() == 1)")
assert.truthy(ok, err)
end)

it("handles coercion to integer", function()
local code = [[
function fn(): integer
local f: float = 1.0
local i: integer = f
return 1
return i
end
]]
local ast, err = parser.parse(code)
Expand All @@ -346,6 +426,89 @@ describe("Titan code generator ", function()
assert.truthy(ok, err)
end)

it("handles unused locals", function()
local code = [[
function fn(): nil
local f: float = 1.0
local i: integer = f
end
]]
local ast, err = parser.parse(code)
assert.truthy(ast, err)
local ok, err = checker.check(ast, code, "test.titan")
assert.truthy(ok, err)
assert.same("Exp_ToInt", ast[1].block.stats[2].exp._tag)
local ok, err = generate(ast, "titan_test")
assert.truthy(ok, err)
end)

it("generates code for integer exported variables", function()
local code = [[
a: integer = 1
function geta(): integer
return a
end
]]
local ast, err = parser.parse(code)
assert.truthy(ast, err)
local ok, err = checker.check(ast, code, "test.titan")
assert.truthy(ok, err)
local ok, err = generate(ast, "titan_test")
assert.truthy(ok, err)
local ok, err = call("titan_test", "assert(titan_test.geta() == 1);titan_test.a = 2;assert(titan_test.geta() == 2)")
assert.truthy(ok, err)
end)

it("generates code for exported float variables", function()
local code = [[
a: float = 1
function geta(): float
return a
end
]]
local ast, err = parser.parse(code)
assert.truthy(ast, err)
local ok, err = checker.check(ast, code, "test.titan")
assert.truthy(ok, err)
local ok, err = generate(ast, "titan_test")
assert.truthy(ok, err)
local ok, err = call("titan_test", "assert(titan_test.geta() == 1);titan_test.a = 2;assert(titan_test.geta() == 2)")
assert.truthy(ok, err)
end)

it("generates code for exported boolean variables", function()
local code = [[
a: boolean = true
function geta(): boolean
return a
end
]]
local ast, err = parser.parse(code)
assert.truthy(ast, err)
local ok, err = checker.check(ast, code, "test.titan")
assert.truthy(ok, err)
local ok, err = generate(ast, "titan_test")
assert.truthy(ok, err)
local ok, err = call("titan_test", "assert(titan_test.geta() == true);titan_test.a = false;assert(titan_test.geta() == false)")
assert.truthy(ok, err)
end)

it("generates code for exported array variables", function()
local code = [[
a: {integer} = {}
function len(): integer
return #a
end
]]
local ast, err = parser.parse(code)
assert.truthy(ast, err)
local ok, err = checker.check(ast, code, "test.titan")
assert.truthy(ok, err)
local ok, err = generate(ast, "titan_test")
assert.truthy(ok, err)
local ok, err = call("titan_test", "assert(titan_test.len() == 0);titan_test.a={1};assert(titan_test.len() == 1)")
assert.truthy(ok, err)
end)
end)


64 changes: 43 additions & 21 deletions titan-compiler/checker.lua
Original file line number Diff line number Diff line change
Expand Up @@ -104,35 +104,59 @@ local function trycoerce(node, target)
end
end

-- First typecheck pass over the module, collects type information
-- for top-level functions and variables and checks for duplicate definitions
-- First typecheck pass over the module, typecheks module variables
-- ast: AST for the whole module
-- st: symbol table
-- errors: list of compile-time errors
-- annotates the top-level nodes with their types in a "_type" field
-- annotates whether a top-level declaration is duplicated with a "_ignore" field
local function firstpass(ast, st, errors)
for _, tlnode in ipairs(ast) do
local tag = tlnode._tag
local name
if tag == "TopLevel_Func" then
if tlnode._tag == "TopLevel_Var" then
name = tlnode.decl.name
if tlnode.decl.type then
tlnode._type = typefromnode(tlnode.decl.type, errors)
checkexp(tlnode.value, st, errors, tlnode._type)
else
checkexp(tlnode.value, st, errors)
tlnode._type = tlnode.value._type
end
tlnode._lin = util.get_line_number(errors.subject, tlnode._pos)
if st:find_dup(name) then
typeerror(errors, "duplicate variable declaration for " .. name, tlnode._pos)
tlnode._ignore = true
else
st:add_symbol(name, tlnode)
end
end
end
end


-- Second typecheck pass over the module, collects type information
-- for top-level functions
-- ast: AST for the whole module
-- st: symbol table
-- errors: list of compile-time errors
-- annotates the top-level nodes with their types in a "_type" field
-- annotates whether a top-level declaration is duplicated with a "_ignore" field
local function secondpass(ast, st, errors)
for _, tlnode in ipairs(ast) do
local name
if tlnode._tag == "TopLevel_Func" then
name = tlnode.name
local ptypes = {}
for _, pdecl in ipairs(tlnode.params) do
table.insert(ptypes, typefromnode(pdecl.type, errors))
end
tlnode._type = types.Function(ptypes, typefromnode(tlnode.rettype, errors))
elseif tag == "TopLevel_Var" then
name = tlnode.decl.name
tlnode._type = typefromnode(tlnode.decl.type, errors)
else
error("impossible")
end
if st:find_dup(name) then
typeerror(errors, "duplicate function or variable declaration for " .. name, tlnode._pos)
tlnode._ignore = true
else
st:add_symbol(name, tlnode)
if st:find_dup(name) then
typeerror(errors, "duplicate function or variable declaration for " .. name, tlnode._pos)
tlnode._ignore = true
else
st:add_symbol(name, tlnode)
end
end
end
end
Expand Down Expand Up @@ -300,14 +324,14 @@ function checkexp(node, st, errors, context)
if tag == "Var_Name" then
local decl = st:find_symbol(node.name)
if not decl then
-- TODO generate better error messages when we have the line num
local msg = "variable '" .. node.name .. "' not declared"
typeerror(errors, msg, node._pos)
node._type = types.Integer
elseif decl._tag == "TopLevel_Func" then
typeerror(errors, "reference to function " .. node.name .. " outside of function call", decl._pos)
node._type = types.Integer
else
decl._used = true
node._decl = decl
node._type = decl._type
end
Expand Down Expand Up @@ -551,19 +575,16 @@ local function checkfunc(node, st, errors)
end
end

-- Second typechecking pass over the module, checks function bodies
-- and rhs of top-level variable declarations
-- Third typechecking pass over the module, checks function bodies
-- ast: AST for the whole module
-- st: symbol table
-- errors: list of compile-time errors
local function secondpass(ast, st, errors)
local function thirdpass(ast, st, errors)
for _, tlnode in ipairs(ast) do
if not tlnode._ignore then
local tag = tlnode._tag
if tag == "TopLevel_Func" then
st:with_block(checkfunc, tlnode, st, errors)
else
checkexp(tlnode.value, st, errors, tlnode._type)
end
end
end
Expand All @@ -581,6 +602,7 @@ function checker.check(ast, subject, filename)
local errors = {subject = subject, filename = filename}
firstpass(ast, st, errors)
secondpass(ast, st, errors)
thirdpass(ast, st, errors)
if #errors > 0 then
return false, table.concat(errors, "\n")
end
Expand Down
Loading

0 comments on commit da8890a

Please sign in to comment.