Skip to content

Commit

Permalink
Merge branch 'dev' of github.com:mewmew/uc into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
sangisos committed May 3, 2016
2 parents 6e9c335 + c8f6f7a commit c49cf0c
Show file tree
Hide file tree
Showing 17 changed files with 227 additions and 22 deletions.
18 changes: 15 additions & 3 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -731,7 +731,11 @@ func (n *TypeDef) Name() *Ident {
//
// *BlockStmt
func (n *FuncDecl) Value() Node {
return n.Body
// ref: https://golang.org/doc/faq#nil_error
if n.Body != nil {
return n.Body
}
return nil
}

// Value returns the initializing value of the defined identifier; or nil if
Expand All @@ -741,7 +745,11 @@ func (n *FuncDecl) Value() Node {
//
// Expr
func (n *VarDecl) Value() Node {
return n.Val
// ref: https://golang.org/doc/faq#nil_error
if n.Val != nil {
return n.Val
}
return nil
}

// Value returns the initializing value of the defined identifier; or nil if
Expand All @@ -751,7 +759,11 @@ func (n *VarDecl) Value() Node {
//
// Type
func (n *TypeDef) Value() Node {
return n.DeclType
// ref: https://golang.org/doc/faq#nil_error
if n.DeclType != nil {
return n.DeclType
}
return nil
}

// isDecl ensures that only declaration nodes can be assigned to the Decl
Expand Down
3 changes: 3 additions & 0 deletions ast/astutil/astutil.go
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,8 @@ import "github.com/mewmew/uc/ast"

// IsDef reports whether the given declaration is a definition.
func IsDef(decl ast.Decl) bool {
if _, ok := decl.(*ast.VarDecl); ok {
return true
}
return decl.Value() != nil
}
Binary file modified report/inc/sections/3_syntactic_analysis/ast_doc.pdf
Binary file not shown.
Binary file not shown.
6 changes: 1 addition & 5 deletions report/sections/4_semantic_analysis/1_design_decisions.tex
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
\subsection{Design Decisions}

% TODO:
% * your representation of types
% - Add GoDoc PDF of the uc/types package to appendix.
% * what information you associate with identifiers in the environment
% * how you chose to implement the environment (symbol table)
% * how you handle delimited scopes (restoring the environment when leaving the scope)
Expand All @@ -16,9 +14,7 @@ \subsubsection{Multi-pass Identifier Resolution}

\subsubsection{User-defined Type Definitions}

Since day one, each stage of the compiler has been designed to support user-defined type definitions. Firstly, the lexer mimicked the implementation of Clang to address \textit{the lexer hack}\footnote{The lexer hack: \url{https://en.wikipedia.org/wiki/The_lexer_hack}} by tokenizing type keywords (e.g. \texttt{int}) as identifiers. Secondly, the grammar used to generate the parser was refined to define basic types in terms of identifiers and later extended to add support for the \texttt{typedef} construct. Lastly, the semantic analysis leveraged identifier resolution to provide a uniform representation of types by adding pre-declared type definitions of keyword types (e.g. \texttt{int}) to the \textit{universe} scope environment (as further described in section \ref{sec:scope}). As user-defined types (e.g. \texttt{typedef int foo;}) are synonyms for their underlying type (i.e. \texttt{foo} is an alias for \texttt{int}, see §6.7.7.3 \cite{c11_spec}), type definitions map identifiers to their underlying types within the type system.

\footnote{Type declarations: \url{https://github.com/mewmew/uc/issues/54}}
Since day one, each stage of the compiler has been designed to support user-defined type definitions. Firstly, the lexer mimicked the implementation of Clang to address \textit{the lexer hack}\footnote{The lexer hack: \url{https://en.wikipedia.org/wiki/The_lexer_hack}} by tokenizing type keywords (e.g. \texttt{int}) as identifiers. Secondly, the grammar used to generate the parser was refined to define basic types in terms of identifiers and later extended to add support for the \texttt{typedef} construct. Lastly, the semantic analysis leveraged identifier resolution to provide a uniform representation of types by adding pre-declared type definitions of keyword types (e.g. \texttt{int}) to the \textit{universe} scope environment (as further described in section \ref{sec:scope}). As user-defined types (e.g. \texttt{typedef int foo;}) are synonyms for their underlying type (i.e. \texttt{foo} is an alias for \texttt{int}, see §6.7.7.3 \cite{c11_spec}), type definitions map identifiers to their underlying types within the type system\footnote{Type declarations: \url{https://github.com/mewmew/uc/issues/54}}.

\subsubsection{Typing Rules}

Expand Down
4 changes: 3 additions & 1 deletion report/sections/4_semantic_analysis/2_implementation.tex
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,9 @@ \subsection{Implementation}
% TODO
... have been cross-referenced against §... of the C11 specification \cite{c11_spec}.

\subsubsection{Type Representation}
\subsubsection{Type Representations}

See Appendix~\ref{app:semantic/types_doc}.

\subsubsection{Typing Rules}

Expand Down
6 changes: 6 additions & 0 deletions report/sections/appendices/4_semantic_analysis.tex
Original file line number Diff line number Diff line change
@@ -1,3 +1,9 @@
\subsection{Type Representations}
\label{app:semantic/types_doc}

\includepdf[pages=-]{inc/sections/4_semantic_analysis/types_doc.pdf}

\clearpage

\subsection{Semantic Analysis Test Cases}
\label{app:semantic/testcases}
Expand Down
6 changes: 5 additions & 1 deletion sem/errors/errors.go
Original file line number Diff line number Diff line change
Expand Up @@ -70,7 +70,11 @@ func (e *Error) Error() string {
// 1 = y
// ^
line, col := src.Position(e.Pos)
srcLine := src.Input[src.Lines[line-1]:src.Lines[line]]
end := len(src.Input)
if len(src.Lines) > line {
end = src.Lines[line]
}
srcLine := src.Input[src.Lines[line-1]:end]
srcLine = strings.Replace(srcLine, "\t", " ", -1)
srcLine = strings.TrimRight(srcLine, "\n\r")
arrow := fmt.Sprintf("%*s", col, "^")
Expand Down
5 changes: 5 additions & 0 deletions sem/resolve.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,11 @@ func resolve(file *ast.File) error {

// First pass, add global declarations to file scope.
fileScope := NewScope(universe)
fileScope.IsDef = func(decl ast.Decl) bool {
// Consider variable declarations as tentative definitions; i.e. return
// false, unless variable definition.
return decl.Value() != nil
}
for _, decl := range file.Decls {
if err := fileScope.Insert(decl); err != nil {
return errutil.Err(err)
Expand Down
12 changes: 9 additions & 3 deletions sem/scope.go
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,18 @@ type Scope struct {
Outer *Scope
// Identifiers declared within the current scope.
Decls map[string]ast.Decl
// IsDef reports whether the given declaration is a definition.
IsDef func(ast.Decl) bool
}

// NewScope returns a new lexical scope immediately surrouded by the given outer
// scope.
func NewScope(outer *Scope) *Scope {
return &Scope{Outer: outer, Decls: make(map[string]ast.Decl)}
return &Scope{
Outer: outer,
Decls: make(map[string]ast.Decl),
IsDef: astutil.IsDef,
}
}

// Insert inserts the given declaration into the current scope.
Expand Down Expand Up @@ -50,13 +56,13 @@ func (s *Scope) Insert(decl ast.Decl) error {

// The last tentative definition becomes the definition, unless defined
// explicitly (e.g. having an initializer or function body).
if !astutil.IsDef(prev) {
if !s.IsDef(prev) {
s.Decls[name] = decl
return nil
}

// Definition already present in scope.
if astutil.IsDef(decl) {
if s.IsDef(decl) {
// TODO: Consider adding support for multiple errors (and potentially
// warnings and notifications).
//
Expand Down
48 changes: 39 additions & 9 deletions sem/sem_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -17,33 +17,44 @@ func TestCheckValid(t *testing.T) {
path string
}{
{path: "../testdata/quiet/semantic/s01.c"},
{path: "../testdata/quiet/semantic/s02.c"},
{path: "../testdata/quiet/semantic/s03.c"},
{path: "../testdata/quiet/semantic/s04.c"},
{path: "../testdata/quiet/semantic/s05.c"},
{path: "../testdata/quiet/semantic/s06.c"},
{path: "../testdata/extra/semantic/missing-return-main.c"},
{path: "../testdata/extra/semantic/tentative-var-def.c"},
{path: "../testdata/extra/semantic/variable-sized-array-arg.c"},
}

errors.UseColor = false

for _, g := range golden {
s, err := scanner.Open(g.path)
buf, err := ioutil.ReadFile(g.path)
if err != nil {
t.Error(err)
t.Errorf("%q: %v", g.path, err)
continue
}
input := string(buf)
s := scanner.NewFromString(input)
src := errors.NewSource(g.path, input)

p := parser.NewParser()
file, err := p.Parse(s)
if err != nil {
t.Error(err)
continue
}
f := file.(*ast.File)

err = sem.Check(f)
if err != nil {
if e, ok := err.(*errutil.ErrInfo); ok {
// Unwrap errutil error.
err = e.Err
if e, ok := err.(*errors.Error); ok {
// Unwrap semantic error.
e.Src = src
}
}
t.Errorf("%q: unexpected error: `%v`", g.path, err.Error())
}
Expand All @@ -55,6 +66,12 @@ func TestCheckError(t *testing.T) {
path string
want string
}{
{
path: "../testdata/quiet/semantic/s02.c",
want: `(../testdata/quiet/semantic/s02.c:3) error: missing return at end of non-void function "foo"
; }
^`,
},
{
path: "../testdata/incorrect/semantic/se01.c",
want: `(../testdata/incorrect/semantic/se01.c:5) error: undeclared identifier "b"
Expand Down Expand Up @@ -278,12 +295,24 @@ void f(int a, void) {
want: `(../testdata/extra/semantic/index-array.c:7) error: invalid array index; expected integer, got "int[20]"
x[y];
^`,
},
{
path: "../testdata/extra/semantic/local-var-redef.c",
want: `(../testdata/extra/semantic/local-var-redef.c:6) error: redefinition of "x"
int x;
^`,
},
{
path: "../testdata/extra/semantic/missing-return.c",
want: `(../testdata/extra/semantic/missing-return.c:10) error: missing return at end of non-void function "f"
}
^`,
},
{
path: "../testdata/extra/semantic/param-redef.c",
want: `(../testdata/extra/semantic/param-redef.c:5) error: redefinition of "x"
int x;
^`,
},
{
path: "../testdata/extra/semantic/unnamed-arg.c",
Expand All @@ -296,12 +325,6 @@ void f(int) {
want: `(../testdata/extra/semantic/variable-sized-array.c:5) error: array size or initializer missing for "y"
char y[];
^`,
},
{
path: "../testdata/extra/semantic/void-arg.c",
want: `(../testdata/extra/semantic/void-arg.c:4) error: "x" has invalid type "void"
void f(void x) {
^`,
},
{
path: "../testdata/extra/semantic/void-array.c",
Expand All @@ -313,6 +336,12 @@ void f(void x) {
path: "../testdata/extra/semantic/void-array-arg.c",
want: `(../testdata/extra/semantic/void-array-arg.c:4) error: invalid element type "void" of array "x"
void f(void x[]) {
^`,
},
{
path: "../testdata/extra/semantic/void-param.c",
want: `(../testdata/extra/semantic/void-param.c:4) error: "x" has invalid type "void"
void f(void x) {
^`,
},
{
Expand Down Expand Up @@ -356,6 +385,7 @@ void f(void, void) {
// Unwrap errutil error.
err = e.Err
if e, ok := err.(*errors.Error); ok {
// Unwrap semantic error.
e.Src = src
}
}
Expand Down
Loading

0 comments on commit c49cf0c

Please sign in to comment.