package attl // ParseFunc is a parser function. // It parses the input input starting from *index, which must be // guaranteed by the caller to be non-nil. // It should return as follows: // * If the parse function matched what it is intended to parse // it should return the parsed value, nil, and index should be moved to // point right after the parsed part of te string. // * If the parse function did not match what it is intended to parse // it should retirn nil, nil, and index should be unchanged. // * If the parse function did match what it is intended to parse // but there is a parse error, it should return nil, *Error, // and index should be set to the error location. type ParseFunc func(input []rune, index *int) (Value, *Error) var Debug = false func debug(msg string) { if Debug { print(msg) } } func ParseAlternative(input []rune, index *int, funcs ...ParseFunc) (Value, *Error) { for _, fun := range funcs { val, err := fun(input, index) if err != nil || val != nil { return val, err } } return nil, nil } func ParseWhileRuneOk(input []rune, index *int, ok func(r rune) bool) (Value, *Error) { length := len(input) start, now := *index, 0 for ; *index < length; *index++ { r := input[*index] if !ok(r) { if now == 0 { return nil, nil } return String(input[start:*index]), nil } now++ } return nil, ErrorFromString("unexpected EOF: >" + string(input[start:*index]) + "<") } type LineInfo struct { Line int From int To int } type LineIndex []LineInfo func PhysicalLineIndex(input []rune) LineIndex { res := LineIndex{} line, last, index := 0, 0, 0 for ; index < len(input); index++ { ch := input[index] if ch == '\n' { line++ li := LineInfo{line, last, index} last = index res = append(res, li) } } li := LineInfo{line, last, index} res = append(res, li) return res } func (li LineIndex) Lookup(index int) (row, col int) { for _, info := range li { if index >= info.From && index < info.To { return info.Line, index - info.From } } return -1, -1 } func Parse(input string) (value Value, rerr *Error) { index := 0 return ParseScript([]rune(input), &index) } func ParseScript(input []rune, index *int) (value Value, rerr *Error) { defer func() { val := recover() err, ok := val.(*Error) if ok { rerr.Children = append(rerr.Children, err) } }() value, rerr = ParseStatements([]rune(input), index) if value != nil { value = Block{value.(List)} } return value, rerr } func IsEof(input []rune, index *int) bool { return *index >= len(input) } func ParseStatements(input []rune, index *int) (Value, *Error) { debug("ParseStatements") statements := List{} for { val, err := ParseStatement(input, index) if err != nil { debug("error in statement") return nil, err } if val != nil { statements = append(statements, val) } sep, err := ParseRs(input, index) if IsEof(input, index) { return statements, nil } if err != nil { debug("error in rs") return nil, err } if sep == nil { return statements, nil } } } func ParseRs(input []rune, index *int) (Value, *Error) { debug("ParseRs") SkipWs(input, index) return ParseWhileRuneOk(input, index, func(r rune) bool { return r == '\n' || r == '\r' || r == ';' }) } func ParseWs(input []rune, index *int) (Value, *Error) { debug("ParseWs") return ParseWhileRuneOk(input, index, func(r rune) bool { return r == ' ' || r == '\t' }) } func ParseWsRs(input []rune, index *int) (Value, *Error) { debug("ParseRs") SkipWs(input, index) return ParseWhileRuneOk(input, index, func(r rune) bool { return r == '\n' || r == '\r' || r == ';' || r == ' ' || r == '\t' }) } func SkipWs(input []rune, index *int) { ParseWs(input, index) } func SkipRs(input []rune, index *int) { ParseRs(input, index) } func SkipWsRs(input []rune, index *int) { ParseWsRs(input, index) } func ParseComment(input []rune, index *int) (Value, *Error) { debug("ParseComment") start := *index if !RequireRune(input, index, '#') { return nil, nil } for ; *index < len(input); *index++ { r := input[*index] if r == '\n' || r == '\r' { end := *index return Comment(string(input[start:end])), nil } } return nil, ErrorFromString("unexpected EOF in comment") } func ParseStatement(input []rune, index *int) (Value, *Error) { debug("ParseStatement") SkipWs(input, index) return ParseAlternative(input, index, ParseCommand, ParseBlock, ParseComment) } func ParseParameters(input []rune, index *int) (Value, *Error) { debug("ParseParameters") params := List{} for { sep, err := ParseWs(input, index) if err != nil { return nil, err } if sep == nil { return params, nil } val, err := ParseParameter(input, index) if err != nil { return nil, err } if val == nil { return params, nil } params = append(params, val) } } func ParseParameter(input []rune, index *int) (Value, *Error) { debug("ParseParameter") funcs := []ParseFunc{ParseLiteral, ParseEvaluation, ParseBlock, ParseGetter} return ParseAlternative(input, index, funcs...) } func ParseOrder(input []rune, index *int) (Value, *Error) { debug("ParseOrder") return ParseAlternative(input, index, ParseLiteral, ParseEvaluation) } func ParseCommand(input []rune, index *int) (Value, *Error) { debug("ParseCommand") order, err := ParseOrder(input, index) if err != nil || order == nil { return order, err } params, err := ParseParameters(input, index) if err != nil { return params, err } if params == nil { params = List{} } return Command{order, params.(List)}, nil } // RequireRune requires a single rune to be present, // and skips it, however that rune is discared. // Returns true if the rune was found, false if not func RequireRune(input []rune, index *int, req rune) bool { if input[*index] == req { *index++ return true } return false } func ParseEvaluation(input []rune, index *int) (Value, *Error) { debug("ParseEvaluation") if !RequireRune(input, index, '[') { return nil, nil } res, err := ParseCommand(input, index) if err != nil { return nil, err } if !RequireRune(input, index, ']') { print(input[*index]) return nil, ErrorFromString("Expected end of evaluation ]") } if res != nil { res = Evaluation{Command: res.(Command)} } return res, nil } func ParseBlock(input []rune, index *int) (Value, *Error) { debug("ParseBlock") if !RequireRune(input, index, '{') { return nil, nil } res, err := ParseStatements(input, index) if err != nil { return nil, err } SkipWsRs(input, index) if !RequireRune(input, index, '}') { return nil, ErrorFromString("Expected end of block }") } return Block{Statements: res.(List)}, nil return nil, nil } func ParseGetter(input []rune, index *int) (Value, *Error) { debug("ParseGetter") if RequireRune(input, index, '$') { if input[*index] == '$' { // recusively parse double getters val, err := ParseGetter(input, index) if err == nil { // Getter with a getter inside. return Getter{val}, err } else { return nil, err } } else { // integer, sring or getter name key, err := ParseLiteral(input, index) if key == nil { return nil, ErrorFromString("Expected literal after getter $") } if err == nil { return Getter{key}, nil } return nil, err } } return nil, nil } func ParseLiteral(input []rune, index *int) (Value, *Error) { debug("ParseLiteral") return ParseAlternative(input, index, ParseWord, ParseString, ParseInteger, ParseRawString) } func IsLetter(r rune) bool { return (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r > rune(128)) || r == '_' || r == '/' } func IsNumber(r rune) bool { return (r >= '0' && r <= '9') } func ParseWord(input []rune, index *int) (Value, *Error) { debug("ParseWord") // a word consists of an ascii letter or non asci characters, or underscore // followed by an ascii letter or number, or non ascii characters, or underscore start := *index r := input[*index] if !IsLetter(r) { return nil, nil } for *index++; *index < len(input); *index++ { r := input[*index] if !(IsLetter(r) || IsNumber(r)) { return Word(string(input[start:*index])), nil } } return nil, ErrorFromString("unexpected EOF in string") } func next(input []rune, index *int) { *index++ if *index >= len(input) { panic(ErrorFromString("Unexpected end of input.")) } } func ParseEscape(input []rune, index *int) (Value, *Error) { res := "" if input[*index] != '\\' { return nil, nil } next(input, index) switch input[*index] { case 'a': res += "\a" case 'b': res += "\b" case 'e': res += "\033" case 'f': res += "\f" case 'n': res += "\n" case 'r': res += "\r" case 't': res += "\t" case '\\': res += "\\" case '"': res += "\"" default: return nil, ErrorFromString("Unknown escape sequence character") } return String(res), nil } func ParseString(input []rune, index *int) (Value, *Error) { debug("ParseString") res := "" ch := input[*index] if ch != '"' { return nil, nil } *index++ for *index < len(input) { ch = input[*index] esc, err := ParseEscape(input, index) if err != nil { return nil, err } if esc != nil { res += string(esc.(String)) } else if ch == '"' { *index++ return String(res), nil } else { res += string(ch) } *index++ } return nil, ErrorFromString("Unexpected end of input.") } func ParseRawString(input []rune, index *int) (Value, *Error) { debug("ParseRawString") res := "" ch := input[*index] if ch != '`' { return nil, nil } *index++ for *index < len(input) { ch = input[*index] if ch == '`' { *index++ return String(res), nil } else { res += string(ch) } *index++ } return nil, ErrorFromString("Unexpected end of input.") } func ParseInteger(input []rune, index *int) (Value, *Error) { debug("ParseInteger") ch := input[*index] neg := 1 res := 0 if ch == '-' { neg = -1 } else if ch == '+' { // do nothing, ignore + as an integer prefix } else { res = int(ch - '0') if res < 0 || res > 9 { // Not a digit, no integer return nil, nil } } *index++ for *index < len(input) { ch = input[*index] ch -= '0' if ch < 0 || ch > 9 { // Not a digit, finished return Int(neg * res), nil } res = res * 10 res = res + int(ch) *index++ } return nil, ErrorFromString("unexpected EOF in number") }