Skip to content

Commit

Permalink
--wip-- [skip ci]
Browse files Browse the repository at this point in the history
  • Loading branch information
gravataLonga committed Jul 11, 2022
1 parent 3401256 commit b85f32c
Show file tree
Hide file tree
Showing 7 changed files with 79 additions and 12 deletions.
22 changes: 21 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -534,4 +534,24 @@ go tool trace trace.out

```
go test -race
```
```

# References

- Structure and Interpretation of Computer Programs, Second Edition
- Engineering a Compiler, Second Edi- tion. Morgan Kaufmann.
- Parsing Techniques. A Practical Guide.. Ellis Horwood Limited.
- Modern Compiler Design, Second Edition. Springer
- The Elements Of Computing Systems. MIT Press.

# Others

```
Program code -> Lexical Analysis (Lexer) -> Syntactic Analysis (Parser) -> Semantic Analysis -> Generated/Optimization -> Machine Code
```

1. Program code (Ninja Code)
2. Lexer -> Produce Tokens
3. Parser -> Produces AST Tree
4. Semantic Analysis -> Syntax of AST
5. Evaluator -> Produce Object
15 changes: 15 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import (
"ninja/object"
"ninja/parser"
"ninja/repl"
"ninja/semantic"
"os"
"strings"
)
Expand Down Expand Up @@ -64,13 +65,20 @@ func execCode(input string, writer io.Writer) {
env := object.NewEnvironment()
l := lexer.New(strings.NewReader(input))
p := parser.New(l)
s := semantic.New()

program := p.ParseProgram()
if len(p.Errors()) > 0 {
printParserErrors(p.Errors(), writer)
return
}

program = s.Analysis(program)
if len(s.Errors()) != 0 {
printSemanticErrorsErrors(s.Errors(), writer)
return
}

evaluated := evaluator.Eval(program, env)
if evaluated != nil {
fmt.Fprintf(writer, evaluated.Inspect())
Expand All @@ -83,3 +91,10 @@ func printParserErrors(errors []string, writer io.Writer) {
fmt.Fprintf(writer, "\t %s\n", msg)
}
}

func printSemanticErrorsErrors(errors []string, writer io.Writer) {
fmt.Fprintf(writer, "🔥 Fire at core! semantic errors:")
for _, msg := range errors {
fmt.Fprintf(writer, "\t %s\n", msg)
}
}
8 changes: 8 additions & 0 deletions repl/repl.go
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,14 @@ func (r *repl) printParserErrors(errors []string) {
}
}

func (r *repl) printSemanticErrors(errors []string) {
r.Output("error", "We got some semantic errors.\n")
r.Output("error", "\tsemantic errors:\n")
for _, msg := range errors {
r.Output("error", "\t\t"+msg+"\n")
}
}

func createSpashScreen() string {

file, err := os.CreateTemp("", "repl_logo")
Expand Down
28 changes: 17 additions & 11 deletions semantic/semantic.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ package semantic
import (
"fmt"
"ninja/ast"
"ninja/token"
)

// Semantic here is where we are going doing some semantic analysis
Expand All @@ -11,6 +12,7 @@ type Semantic struct {
scopeStack Stack
globalVariable map[string]ast.Expression
errors []string
resolvingVar string
}

func New() *Semantic {
Expand All @@ -25,8 +27,8 @@ func (s *Semantic) NewError(format string, a ...interface{}) {
s.errors = append(s.errors, fmt.Sprintf(format, a...))
}

func (s *Semantic) Analysis(node *ast.Program) ast.Node {
return s.analysis(node)
func (s *Semantic) Analysis(node *ast.Program) *ast.Program {
return s.analysis(node).(*ast.Program)
}

// newScope record scope how deep is
Expand Down Expand Up @@ -59,22 +61,25 @@ func (s *Semantic) resolve(name string) {
*peek = Scope{name: true}
}

func (s *Semantic) expectIdentifierDeclare(node *ast.Identifier) bool {
name := node.Value
func (s *Semantic) expectIdentifierDeclare(name string, tok token.Token) bool {
peek, ok := s.scopeStack.Peek()
if !ok {
s.NewError("There aren't any scope active %s", name)
return false
}

v, ok := (*peek)[name]
if s.resolvingVar == "" {
return true
}

v, ok := (*peek)[s.resolvingVar]
if !ok {
s.NewError("Variable \"%s\" not declare yet %s", name, node.Token)
s.NewError("Variable \"%s\" not declare yet %s", name, tok)
return false
}

if !v {
s.NewError("Can't read local variable \"%s\" in its own initializer %s", name, node.Token)
s.NewError("Can't read local variable \"%s\" in its own initializer %s", name, tok)
return false
}

Expand All @@ -101,12 +106,13 @@ func (s *Semantic) analysis(node ast.Node) ast.Node {
s.analysis(v)
}
case *ast.Identifier:
s.expectIdentifierDeclare(node)
s.expectIdentifierDeclare(node.Value, node.Token)
s.resolvingVar = ""
case *ast.VarStatement:
s.resolvingVar = node.Name.Value

s.declare(node.Name.Value)
if _, ok := node.Value.(ast.Expression); ok {
s.analysis(node.Value)
}
s.analysis(node.Value)
s.resolve(node.Name.Value)
case *ast.ExpressionStatement:
s.analysis(node.Expression)
Expand Down
3 changes: 3 additions & 0 deletions semantic/semantic_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,9 @@ func TestScopeVariable(t *testing.T) {
{
`function () { var a = !true; }`,
},
{
`var a = 1; function () { var b = a; }`,
},
}

for i, tt := range tests {
Expand Down
4 changes: 4 additions & 0 deletions semantic/stack.go
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,7 @@ func (s *Stack) Peek() (*Scope, bool) {
scope := (*s)[index]
return scope, true
}

func (s *Scope) Put(name string, ready bool) {
(*s)[name] = ready
}
11 changes: 11 additions & 0 deletions semantic/stack_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,14 @@ func TestStack_Peek(t *testing.T) {
t.Errorf("Popped value wasn't change")
}
}

func TestScope_Put(t *testing.T) {
scope := Scope{}
scope.Put("test", true)

v := scope["test"]

if !v {
t.Fatalf("scope test isn't true")
}
}

0 comments on commit b85f32c

Please sign in to comment.