예제 #1
0
파일: lexer.go 프로젝트: danwakefield/gosh
func (l *Lexer) VariableSimple() {
	// When we enter this state we know we have at least one readable
	// char for the varname. That means that any character not valid
	// just terminates the parsing and we dont have to
	// worry about the case of an empty varname
	l.buffer.WriteRune(SentinalSubstitution)
	sv := SubVariable{SubType: VarSubNormal}
	varbuf := bytes.Buffer{}

	c := l.nextChar()
	switch {
	case char.IsSpecial(c):
		varbuf.WriteRune(c)
	case char.IsDigit(c):
		// Positional argv
		for {
			varbuf.WriteRune(c)
			c = l.nextChar()
			if !char.IsDigit(c) {
				l.backup()
				break
			}
		}
	case char.IsFirstInVarName(c):
		for {
			varbuf.WriteRune(c)
			c = l.nextChar()
			if !char.IsInVarName(c) {
				l.backup()
				break
			}
		}
	default:
		l.backup()
	}

	sv.VarName = varbuf.String()
	l.subs = append(l.subs, sv)
	return
}
예제 #2
0
파일: lexer.go 프로젝트: danwakefield/gosh
func (l *Lexer) Substitution() {
	// Upon entering we have only read the '$'
	// Perform the lex of a single complete substitution before returning
	// control to the calling location
	c := l.nextChar()

	switch {
	default:
		l.buffer.WriteRune('$')
		l.backup()
	case c == '(':
		if l.hasNext('(') {
			l.Arith()
		} else {
			l.Subshell()
		}
	case char.IsFirstInVarName(c), char.IsDigit(c), char.IsSpecial(c):
		l.backup()
		l.VariableSimple()
	case c == '{':
		l.VariableComplex()
	}
}
예제 #3
0
파일: lexer.go 프로젝트: danwakefield/gosh
func (l *Lexer) VariableComplex() {
	// Upon entering we have read the opening '{'
	l.buffer.WriteRune(SentinalSubstitution)
	sv := SubVariable{}
	varbuf := bytes.Buffer{}

	defer func() {
		// We defer this as there are multiple return points
		sv.VarName = varbuf.String()
		l.subs = append(l.subs, sv)
	}()

	if l.hasNext('#') {
		// The NParam Special Var
		if l.hasNext('}') {
			varbuf.WriteRune('#')
			return
		}

		// Length variable operator
		sv.SubType = VarSubLength
	}

	c := l.nextChar()
	switch {
	case char.IsSpecial(c):
		varbuf.WriteRune(c)
	case char.IsDigit(c):
		for {
			varbuf.WriteRune(c)
			c = l.nextChar()
			if !char.IsDigit(c) {
				l.backup()
				break
			}
		}
	case char.IsFirstInVarName(c):
		for {
			varbuf.WriteRune(c)
			c = l.nextChar()
			if !char.IsInVarName(c) {
				l.backup()
				break
			}
		}
	case c == EOFRune:
		l.backup()
		return
	}

	// Either a Enclosed variable '${foo}' or a length operation '${#foo}'
	if l.hasNext('}') {
		return
	}

	// Length operator should have returned since only ${#varname} is valid
	if sv.SubType == VarSubLength {
		l.log.Error("Line %d: Bad substitution (%s)", l.lineNo, l.input[l.lastPosition:l.position])
		os.Exit(1)
	}

	if l.hasNext(':') {
		sv.CheckNull = true
	}

	switch l.nextChar() {
	case '-':
		sv.SubType = VarSubMinus
	case '+':
		sv.SubType = VarSubPlus
	case '?':
		sv.SubType = VarSubQuestion
	case '=':
		sv.SubType = VarSubAssign
	case '#':
		if l.hasNext('#') {
			sv.SubType = VarSubTrimLeftMax
		} else {
			sv.SubType = VarSubTrimLeft
		}
	case '%':
		if l.hasNext('%') {
			sv.SubType = VarSubTrimRightMax
		} else {
			sv.SubType = VarSubTrimRight
		}
	default:
		l.log.Error("Line %d: Bad substitution (%s)", l.lineNo, l.input[l.lastPosition:l.position])
		os.Exit(1)
	}

	// Read until '}'
	// In the future to support Nested vars etc create new sublexer from
	// l.input[l.pos:] and take the first lexitem as the sub val then adjust
	// this lexer's position and trash sublexer
	c = l.nextChar()
	subValBuf := bytes.Buffer{}
	for {
		if c == '}' {
			break
		}
		subValBuf.WriteRune(c)
		c = l.nextChar()
	}
	sv.SubVal = subValBuf.String()
}
예제 #4
0
파일: lexer.go 프로젝트: danwakefield/gosh
// Lex returns the next Token in the input string and an interface value.
// The interface will also contain a value dependant on the Token
// If Token == ArithNumber then interface will be an int64
// If Token == ArithVariable then interface will be a string
// If Token == ArithError then interface will be an error
func (l *Lexer) Lex() (Token, interface{}) {
	var t Token
	var checkAssignmentOp bool
	var startPos, endPos int

	c := l.next()

	// Ignore whitespace
	for {
		if c == ' ' || c == '\n' || c == '\t' {
			c = l.next()
		} else {
			break
		}
	}

	if c == EOFRune {
		return ArithEOF, nil
	}

	if char.IsDigit(c) {
		return lexDigit(l, c)
	}

	// Finds variable names.
	if char.IsFirstInVarName(c) {
		startPos = l.pos - l.lastRuneWidth
		endPos = l.pos
		for {
			if l.hasNextFunc(char.IsInVarName) {
				endPos++
			} else {
				break
			}
		}
		return ArithVariable, l.input[startPos:endPos]
	}

	switch c {
	case '>':
		switch l.next() {
		case '>':
			t = ArithRightShift
			checkAssignmentOp = true
		case '=':
			t = ArithGreaterEqual
		default:
			t = ArithGreaterThan
			l.backup()
		}
	case '<':
		switch l.next() {
		case '<':
			t = ArithLeftShift
			checkAssignmentOp = true
		case '=':
			t = ArithLessEqual
		default:
			t = ArithLessThan
			l.backup()
		}
	case '|':
		if l.hasNext('|') {
			t = ArithOr
		} else {
			t = ArithBinaryOr
			checkAssignmentOp = true
		}
	case '&':
		if l.hasNext('&') {
			t = ArithAnd
		} else {
			t = ArithBinaryAnd
			checkAssignmentOp = true
		}
	case '*':
		t = ArithMultiply
		checkAssignmentOp = true
	case '/':
		t = ArithDivide
		checkAssignmentOp = true
	case '%':
		t = ArithRemainder
		checkAssignmentOp = true
	case '+':
		t = ArithAdd
		checkAssignmentOp = true
	case '-':
		t = ArithSubtract
		checkAssignmentOp = true
	case '^':
		t = ArithBinaryXor
		checkAssignmentOp = true
	case '!':
		if l.hasNext('=') {
			t = ArithNotEqual
		} else {
			t = ArithNot
		}
	case '=':
		if l.hasNext('=') {
			t = ArithEqual
		} else {
			t = ArithAssignment
		}
	case '(':
		t = ArithLeftParen
	case ')':
		t = ArithRightParen
	case '~':
		t = ArithBinaryNot
	case '?':
		t = ArithQuestionMark
	case ':':
		t = ArithColon
	default:
		t = ArithError
	}

	if checkAssignmentOp {
		if l.hasNext('=') {
			t += ArithAssignDiff
		}
	}

	return t, nil
}