forked from mitchellh/go-mruby
-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.go
127 lines (105 loc) · 2.77 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 mruby
import (
"bytes"
"fmt"
"unsafe"
)
// #include <stdlib.h>
// #include "gomruby.h"
import "C"
// Parser is a parser for Ruby code.
type Parser struct {
code string
mrb *Mrb
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(m *Mrb) *Parser {
p := C.mrb_parser_new(m.state)
// Set capture_errors to true so we don't go just printing things
// out to stdout.
C._go_mrb_parser_set_capture_errors(p, 1)
return &Parser{
mrb: m,
parser: p,
}
}
// 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() *MrbValue {
proc := C.mrb_generate_code(p.mrb.state, p.parser)
return newValue(p.mrb.state, 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, c *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 c != nil {
ctx = c.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 := 0; i < nwarn; i++ {
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 := 0; i < nerr; i++ {
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()
}