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 }
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() } }
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() }
// 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 }