forked from mitchellh/go-mruby
-
Notifications
You must be signed in to change notification settings - Fork 2
/
parser.go
127 lines (105 loc) · 2.79 KB
/
parser.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package gruby
// #include "gruby.h"
import "C"
import (
"bytes"
"fmt"
"unsafe"
)
// Parser is a parser for Ruby code.
type Parser struct {
code string
grb *GRuby
parser *C.struct_mrb_parser_state
}
// NewParser initializes the resources for a parser.
//
// Make sure to Close the parser when you're done with it.
func NewParser(grb *GRuby) *Parser {
parser := C.mrb_parser_new(grb.state)
// Set capture_errors to true so we don't go just printing things
// out to stdout.
C._go_mrb_parser_set_capture_errors(parser, C._go_mrb_int2bool(1))
return &Parser{
code: "",
grb: grb,
parser: parser,
}
}
// Close releases any resources associated with the parser.
func (p *Parser) Close() {
C.mrb_parser_free(p.parser)
// Empty out the code so the other string can get GCd
p.code = ""
}
// GenerateCode takes all the internal parser state and generates
// executable Ruby code, returning the callable proc.
func (p *Parser) GenerateCode() Value {
proc := C.mrb_generate_code(p.grb.state, p.parser)
return p.grb.value(C.mrb_obj_value(unsafe.Pointer(proc)))
}
// Parse parses the code in the given context, and returns any warnings
// or errors from parsing.
//
// The CompileContext can be nil to not set a context.
func (p *Parser) Parse(code string, cctx *CompileContext) ([]*ParserMessage, error) {
// We set p.code so that the string doesn't get garbage collected
var s *C.char = C.CString(code)
p.code = code
p.parser.s = s
p.parser.send = C._go_mrb_calc_send(s)
var ctx *C.mrbc_context
if cctx != nil {
ctx = cctx.ctx
}
C.mrb_parser_parse(p.parser, ctx)
var warnings []*ParserMessage
if p.parser.nwarn > 0 {
nwarn := int(p.parser.nwarn)
warnings = make([]*ParserMessage, nwarn)
for i := range nwarn {
msg := p.parser.warn_buffer[i]
warnings[i] = &ParserMessage{
Col: int(msg.column),
Line: int(msg.lineno),
Message: C.GoString(msg.message),
}
}
}
if p.parser.nerr > 0 {
nerr := int(p.parser.nerr)
errors := make([]*ParserMessage, nerr)
for i := range nerr {
msg := p.parser.error_buffer[i]
errors[i] = &ParserMessage{
Col: int(msg.column),
Line: int(msg.lineno),
Message: C.GoString(msg.message),
}
}
return warnings, &ParserError{Errors: errors}
}
return warnings, nil
}
// ParserMessage represents a message from parsing code: a warning or
// error.
type ParserMessage struct {
Col int
Line int
Message string
}
// ParserError is an error from the parser.
type ParserError struct {
Errors []*ParserMessage
}
func (p ParserError) Error() string {
return p.String()
}
func (p ParserError) String() string {
var buf bytes.Buffer
buf.WriteString("Ruby parse error!\n\n")
for _, e := range p.Errors {
buf.WriteString(fmt.Sprintf("line %d:%d: %s\n", e.Line, e.Col, e.Message))
}
return buf.String()
}