Browse Source

add proper indent/dedent handling

master
Brett Langdon 10 years ago
parent
commit
82746ab67f
1 changed files with 130 additions and 15 deletions
  1. +130
    -15
      scanner/scanner.go

+ 130
- 15
scanner/scanner.go View File

@ -12,24 +12,39 @@ var EOF rune = 0
var MAXINDENT int = 100 var MAXINDENT int = 100
type Scanner struct { type Scanner struct {
state errorcode.ErrorCode
reader *bufio.Reader
currentPosition *Position
positionBuffer []*Position
currentLine int
currentColumn int
asyncDef bool
asyncDef bool
atBol bool
currentColumn int
currentLine int
currentPosition *Position
indentationAltStack []int
indentationCurrent int
indentationLevel int
indentationPending int
indentationStack []int
positionBuffer []*Position
tokenBuffer []*token.Token
reader *bufio.Reader
state errorcode.ErrorCode
tabsize int
tabsizeAlt int
} }
func NewScanner(r io.Reader) *Scanner { func NewScanner(r io.Reader) *Scanner {
return &Scanner{ return &Scanner{
state: errorcode.E_OK,
reader: bufio.NewReader(r),
positionBuffer: make([]*Position, 0),
currentLine: 1,
currentColumn: 0,
atBol: true,
currentColumn: 0,
currentLine: 1,
indentationAltStack: make([]int, MAXINDENT),
indentationCurrent: 0,
indentationLevel: 0,
indentationPending: 0,
indentationStack: make([]int, MAXINDENT),
positionBuffer: make([]*Position, 0),
tokenBuffer: make([]*token.Token, 0),
reader: bufio.NewReader(r),
state: errorcode.E_OK,
tabsize: 8,
} }
} }
@ -193,10 +208,109 @@ func (scanner *Scanner) parseQuoted(positions *Positions, quote rune) *token.Tok
return positions.AsToken(token.STRING) return positions.AsToken(token.STRING)
} }
func (scanner *Scanner) unreadToken(tok *token.Token) {
scanner.tokenBuffer = append(scanner.tokenBuffer, tok)
}
func (scanner *Scanner) NextToken() *token.Token { func (scanner *Scanner) NextToken() *token.Token {
if len(scanner.tokenBuffer) > 0 {
last := len(scanner.tokenBuffer) - 1
nextToken := scanner.tokenBuffer[last]
scanner.tokenBuffer = scanner.tokenBuffer[0:last]
return nextToken
}
blankline := false
positions := NewPositions() positions := NewPositions()
var pos *Position
pos := scanner.nextPosition()
if scanner.atBol {
// Get indentation level
col := 0
altcol := 0
scanner.atBol = false
pos = scanner.nextPosition()
for {
if pos.Char == ' ' {
col++
altcol++
} else if pos.Char == '\t' {
col = (col/scanner.tabsize + 1) * scanner.tabsize
altcol = (altcol/scanner.tabsizeAlt + 1) * scanner.tabsizeAlt
} else {
break
}
pos = scanner.nextPosition()
}
scanner.unreadPosition(pos)
if pos.Char == '#' || pos.Char == '\n' {
// Lines with only newline or comment, shouldn't affect indentation
if col == 0 && pos.Char == '\n' {
blankline = false
} else {
blankline = true
}
}
if !blankline && scanner.indentationLevel == 0 {
if col == scanner.indentationStack[scanner.indentationCurrent] {
if altcol != scanner.indentationAltStack[scanner.indentationCurrent] {
return positions.AsToken(token.ERRORTOKEN)
}
} else if col > scanner.indentationStack[scanner.indentationCurrent] {
if scanner.indentationCurrent+1 >= MAXINDENT {
return positions.AsToken(token.ERRORTOKEN)
}
if altcol <= scanner.indentationAltStack[scanner.indentationCurrent] {
return positions.AsToken(token.ERRORTOKEN)
}
scanner.indentationPending++
scanner.indentationCurrent++
scanner.indentationStack[scanner.indentationCurrent] = col
scanner.indentationAltStack[scanner.indentationCurrent] = altcol
} else {
for scanner.indentationCurrent > 0 && col < scanner.indentationStack[scanner.indentationCurrent] {
scanner.indentationPending--
scanner.indentationCurrent--
}
if col != scanner.indentationStack[scanner.indentationCurrent] {
return positions.AsToken(token.ERRORTOKEN)
}
if altcol != scanner.indentationAltStack[scanner.indentationCurrent] {
return positions.AsToken(token.ERRORTOKEN)
}
}
}
}
if scanner.indentationPending != 0 {
if scanner.indentationPending < 0 {
scanner.indentationPending++
pos = scanner.currentPosition
return &token.Token{
ID: token.DEDENT,
LineStart: pos.Line,
ColumnStart: pos.Column,
LineEnd: pos.Line,
ColumnEnd: pos.Column,
Literal: "",
}
} else {
scanner.indentationPending--
pos = scanner.currentPosition
return &token.Token{
ID: token.INDENT,
LineStart: pos.Line,
ColumnStart: pos.Column,
LineEnd: pos.Line,
ColumnEnd: pos.Column + 4,
Literal: " ",
}
}
}
pos = scanner.nextPosition()
// skip spaces // skip spaces
for { for {
if pos.Char != ' ' && pos.Char != '\t' { if pos.Char != ' ' && pos.Char != '\t' {
@ -270,6 +384,7 @@ func (scanner *Scanner) NextToken() *token.Token {
return positions.AsToken(token.NAME) return positions.AsToken(token.NAME)
case ch == '\n': case ch == '\n':
scanner.atBol = true
return positions.AsToken(token.NEWLINE) return positions.AsToken(token.NEWLINE)
case ch == '.': case ch == '.':
pos2 := scanner.nextPosition() pos2 := scanner.nextPosition()


Loading…
Cancel
Save