Skip to content

Commit

Permalink
Working with print and expression statements.
Browse files Browse the repository at this point in the history
  • Loading branch information
iamsayantan committed Apr 11, 2022
1 parent 23f036a commit ec12ffc
Show file tree
Hide file tree
Showing 5 changed files with 97 additions and 17 deletions.
4 changes: 2 additions & 2 deletions glox.go
Original file line number Diff line number Diff line change
Expand Up @@ -79,13 +79,13 @@ func (r *Runtime) run(source string) {
tokens := scanner.ScanTokens()

parser := NewParser(tokens, r)
expr := parser.Parse()
statements := parser.Parse()

if r.hadError {
return
}

interpreter.Interpret(expr)
interpreter.Interpret(statements)
}

func (r *Runtime) report(line int, where string, message string) {
Expand Down
41 changes: 36 additions & 5 deletions interpreter.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,15 +27,46 @@ func NewRuntimeError(token Token, message string) error {
return &RuntimeError{token: token, message: message}
}

func (i *Interpreter) Interpret(expr Expr) {
val, err := i.evaluate(expr)
func (i *Interpreter) Interpret(statements []Stmt) {
for _, stmt := range statements {
err := i.execute(stmt)
if err != nil {

i.runtime.runtimeError(err)
return
}
}
}

func (i *Interpreter) execute(stmt Stmt) error {
err := stmt.Accept(i)
if err != nil {
return err
}

i.runtime.runtimeError(err)
return
return nil
}

// VisitExpressionExpr interprets expression statements. As statements do not
// produce any value, we are discarding the expression generated from evaluating
// the statement's expression.
func (i *Interpreter) VisitExpressionExpr(expr *Expression) error {
_, err := i.evaluate(expr.Expression)
if err != nil {
return err
}

return nil
}

func (i *Interpreter) VisitPrintExpr(expr *Print) error {
val, err := i.evaluate(expr.Expression)
if err != nil {
return err
}

fmt.Println(i.stringify(val))
return nil
}

func (i *Interpreter) stringify(val interface{}) string {
Expand All @@ -47,7 +78,7 @@ func (i *Interpreter) stringify(val interface{}) string {
return fmt.Sprintf("%d", int(val.(float64)))
}

return fmt.Sprintf("%v", val)
return fmt.Sprint(val)
}

func (i *Interpreter) VisitBinaryExpr(expr *Binary) (interface{}, error) {
Expand Down
56 changes: 52 additions & 4 deletions parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,13 +29,61 @@ func NewParser(tokens []Token, runtime *Runtime) *Parser {
}
}

func (p *Parser) Parse() Expr {
func (p *Parser) Parse() []Stmt {
statements := make([]Stmt, 0)
for !p.isAtEnd() {
expr, err := p.statement()
if err != nil {
return nil
}

statements = append(statements,expr)
}

return statements
}

func (p *Parser) statement() (Stmt, error) {
if p.match(PRINT) {
return p.printStatement()
}

return p.expressionStatement()
}

// printStatement parses a print statement. Since the print keyword is
// already consumed by the match method earlier, we just parse the
// subsequent expression, consume the terminating semicolon and emit the
// syntax tree.
func (p *Parser) printStatement() (Stmt, error) {
expr, err := p.expression()
if err != nil {
return nil, err
}

_, err = p.consume(Semicolon, "Expect ; after value.")
if err != nil {
return nil, err
}

return &Print{Expression: expr}, nil
}

// expressionStatement parses expression statements. It kind of acts like a
// fallthrough condition. If we can't match with any known statements, we
// assume it's a expression statement.
func (p *Parser) expressionStatement() (Stmt, error) {
expr, err := p.expression()
if err != nil {
return nil
return nil, err
}

_, err = p.consume(Semicolon, "Expect ; after value.")
if err != nil {
return nil, err
}

return expr
return &Expression{Expression: expr}, nil
}

// expression parses the grammar
Expand Down Expand Up @@ -282,7 +330,7 @@ func (p *Parser) synchronize() {
}

switch p.peek().Type {
case Class, Fun, Var, For, If, While, Print, Return:
case Class, Fun, Var, For, If, While, PRINT, Return:
return
}

Expand Down
1 change: 1 addition & 0 deletions scanner.go
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ func (sc *Scanner) scanString() {

// Trim the surrounding quotes and just take the string literal.
val := sc.sourceRunes[sc.start+1 : sc.current-1]

sc.addToken(String, val)
}

Expand Down
12 changes: 6 additions & 6 deletions stmt.go
Original file line number Diff line number Diff line change
Expand Up @@ -3,29 +3,29 @@ package glox
// Stmt is the interface for lox statements. There are no place in the grammar
// where both expressions and statements are allowed. E.g. the both operands for
// the + operator must be expressions, the body of while loop is always statements.
// Making a separate interface for statements will forbid us to pass statements
// Making a separate interface for statements will forbid us to pass statements
// where an expression was required or vice versa.
type Stmt interface {
Accept(visitor StmtVisitor) (interface{}, error)
Accept(visitor StmtVisitor) error
}

type StmtVisitor interface {
VisitExpressionExpr(expr *Expression) (interface{}, error)
VisitPrintExpr(expr *Print) (interface{}, error)
VisitExpressionExpr(expr *Expression) error
VisitPrintExpr(expr *Print) error
}

type Expression struct {
Expression Expr
}

func (e *Expression) Accept(visitor StmtVisitor) (interface{}, error) {
func (e *Expression) Accept(visitor StmtVisitor) error {
return visitor.VisitExpressionExpr(e)
}

type Print struct {
Expression Expr
}

func (p *Print) Accept(visitor StmtVisitor) (interface{}, error) {
func (p *Print) Accept(visitor StmtVisitor) error {
return visitor.VisitPrintExpr(p)
}

0 comments on commit ec12ffc

Please sign in to comment.