-
Notifications
You must be signed in to change notification settings - Fork 34
Parse pallene types file #657
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: master
Are you sure you want to change the base?
Changes from 4 commits
11c8a06
ae6c982
55dd8f4
c04903a
e31345a
e55eac6
93fe56a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -54,7 +54,14 @@ local Typechecker = util.Class() | |
| -- On failure, returns false and a list of compilation errors | ||
| function typechecker.check(prog_ast) | ||
| local ok, ret = trycatch.pcall(function() | ||
| return Typechecker.new():check_program(prog_ast) | ||
| local type_checker = Typechecker.new() | ||
| if prog_ast._tag == "ast.Program.Program" then | ||
| return type_checker:check_program(prog_ast) | ||
| elseif prog_ast._tag == "ast.TypeFile.Decls" then | ||
|
||
| return type_checker:check_type_file(prog_ast) | ||
| else | ||
| error("unexpected AST root: " .. tostring(prog_ast._tag)) | ||
| end | ||
| end) | ||
| if ok then | ||
| prog_ast = ret | ||
|
|
@@ -317,6 +324,55 @@ function Typechecker:check_program(prog_ast) | |
| return prog_ast | ||
| end | ||
|
|
||
| function Typechecker:check_type_file(prog_ast) | ||
|
|
||
| assert(prog_ast._tag == "ast.TypeFile.Decls") | ||
|
|
||
| -- 1) Add primitive types to the symbol table | ||
| self:add_type_symbol("any", types.T.Any) | ||
| self:add_type_symbol("boolean", types.T.Boolean) | ||
| self:add_type_symbol("float", types.T.Float) | ||
| self:add_type_symbol("integer", types.T.Integer) | ||
| self:add_type_symbol("string", types.T.String) | ||
|
|
||
| -- Check toplevel | ||
| for _, decl in ipairs(prog_ast.decls) do | ||
| local tag = decl._tag | ||
|
|
||
| if tag == "ast.TypeFile.Typealias" then | ||
| local typ = types.T.Alias(decl.name, self:from_ast_type(decl.type)) | ||
| -- self:export_type_symbol(decl.name, typ, decl.loc) | ||
| self:add_type_symbol(decl.name, typ) | ||
| decl._type = typ | ||
|
|
||
| elseif tag == "ast.TypeFile.Record" then | ||
| local field_names = {} | ||
| local field_types = {} | ||
| for _, field_decl in ipairs(decl.field_decls) do | ||
| local field_name = field_decl.name | ||
| if field_types[field_name] then | ||
| type_error(decl.loc, "duplicate field name '%s' in record type", field_name) | ||
| end | ||
| table.insert(field_names, field_name) | ||
| field_types[field_name] = self:from_ast_type(field_decl.type) | ||
| end | ||
|
|
||
| local typ = types.T.Record(decl.name, field_names, field_types, false) | ||
| self:add_type_symbol(decl.name, typ) | ||
|
|
||
| decl._type = typ | ||
|
|
||
| elseif tag == "ast.TypeFile.Decl" then | ||
| local typ = self:from_ast_type(decl.type) | ||
| decl._type = typ | ||
| else | ||
| tagged_union.error(tag) | ||
| end | ||
| end | ||
|
|
||
| return prog_ast | ||
| end | ||
|
|
||
| -- If the last expression in @rhs is a function call that returns multiple values, add ExtraRet | ||
| -- nodes to the end of the list. | ||
| function Typechecker:expand_function_returns(rhs) | ||
|
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -49,6 +49,56 @@ function util.split_ext(file_name) | |
| return name, ext | ||
| end | ||
|
|
||
| --- Return a list consisting of the base name and all extensions of a file | ||
| --- e.g. "file.d.pln" -> { "pln", "d", "file" } | ||
| --- | ||
| --- @param file_name string | ||
| --- @return [string] | ||
| --- | ||
| function util.split_all_ext(file_name) | ||
| local name, ext = util.split_ext(file_name) | ||
| if not ext then | ||
| return { file_name } | ||
| else | ||
| local exts = util.split_all_ext(name) | ||
| table.insert(exts, ext) | ||
| return exts | ||
| end | ||
| end | ||
|
|
||
| local recognized_extensions = { | ||
| ["pln"] = true, | ||
| ["d.pln"] = true, | ||
| ["c"] = true, | ||
| ["lua"] = true, | ||
| ["so"] = true, | ||
| } | ||
|
|
||
| --- Splits the file name into two parts: the base name, the Pallene extensions. | ||
| --- If the file is not known to Pallene, returns nil. | ||
| --- | ||
| --- | ||
| --- e.g. | ||
| --- - "file.pln" -> "file", "pln" | ||
| --- - "file.d.pln" -> "file", "d.pln" | ||
| --- - "dotted.file.d.pln" -> "dotted.file", "d.pln" | ||
| --- - "file.txt" -> nil | ||
| --- | ||
| ---@param file_name string | ||
| ---@return string | nil | ||
| ---@return string | nil | ||
| --- | ||
| function util.split_pallene_ext(file_name) | ||
| local parts = util.split_all_ext(file_name) | ||
| if #parts >= 3 and parts[#parts] == "pln" and parts[#parts - 1] == "d" then | ||
| local base_name = table.concat(parts, ".", 1, #parts - 2) | ||
| return base_name, "d.pln" | ||
| elseif #parts >= 2 and recognized_extensions[parts[#parts]] then | ||
| local base_name = table.concat(parts, ".", 1, #parts - 1) | ||
| return base_name, parts[#parts] | ||
| end | ||
| end | ||
|
|
||
|
||
| function util.get_file_contents(file_name) | ||
| local f, err = io.open(file_name, "r") | ||
| if not f then | ||
|
|
@@ -126,6 +176,10 @@ function util.Class() | |
| return cls | ||
| end | ||
|
|
||
| -- | ||
| -- General purpose utilities | ||
| -- | ||
|
|
||
| function util.expand_type_aliases(ast_node, visited) | ||
| local types = require "pallene.types" | ||
| visited = visited or {} | ||
|
|
||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we encounter an invalid token, the while loop will exit and this function will return as if there was not a problem.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Should we still throw an error if we don't find at least one declaration? If so, we can go with a
repeat-untilThere was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We should allow empty files, in case the module exports nothing. (Also test that...)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I tried to write the error test in a
assert_program_error()fashion, but I couldn't get it to work. So I wrote it another way (src/spec/parser_spec.lua:979-990)