mvsk 1.0.1 → 1.0.2
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/bin/mvsk +42 -0
- data/lib/musk/lexer/helper.rb +27 -0
- data/lib/musk/lexer/keywords.rb +51 -0
- data/lib/musk/lexer.rb +309 -0
- data/lib/musk/nodes.rb +214 -0
- data/lib/musk/parser.rb +170 -0
- data/lib/musk/token.rb +48 -0
- data/lib/musk/types.rb +24 -0
- data/lib/musk/version.rb +3 -0
- data/lib/musk.rb +79 -0
- data/lib/mvsk.rb +1 -2
- metadata +29 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: f04de2ea639c5201033077c0853085693663dcb0c8ffe2bcc4dde87ae0824fc2
|
4
|
+
data.tar.gz: 609ab7ff6ef10298d6d263a77b6594f4575e16c967116ace4bc2d4c328ab5fb5
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 25dd4c8a0a1a9891f287ec03bfe849556994e5c6ba34e374cf2abd01d2e38f949d8fd766b7f190735bb132b15d4c79ff0b9755f9b4d997db35bd2ead960a9535
|
7
|
+
data.tar.gz: 8691243c770fb63eb12eb502a9dbd09b7448d6d7808b0ca5680efa7868f639916b358533fa89a5b1f102faac7c1cc591908ab38eb2565443219326c5cf2f7015
|
data/bin/mvsk
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require 'musk'
|
3
|
+
|
4
|
+
args = ARGV.reject { |s| s.start_with?('-') }
|
5
|
+
opts = ARGV - args
|
6
|
+
|
7
|
+
if args.length == 1 and opts.empty?
|
8
|
+
Musk.run_file(args.first)
|
9
|
+
elsif ARGV.length.zero?
|
10
|
+
Musk.run_prompt
|
11
|
+
else
|
12
|
+
require 'pastel'
|
13
|
+
require 'optparse'
|
14
|
+
|
15
|
+
OptionParser.new do |op|
|
16
|
+
op.version = Musk::VERSION
|
17
|
+
op.banner = <<~USAGE
|
18
|
+
#{0}
|
19
|
+
|
20
|
+
Usage:
|
21
|
+
|
22
|
+
mvsk <file> [options]
|
23
|
+
|
24
|
+
Options:
|
25
|
+
USAGE
|
26
|
+
|
27
|
+
op.on '--tokens', 'print tokens' do
|
28
|
+
puts 'tokens'
|
29
|
+
end
|
30
|
+
|
31
|
+
op.on '--nodes', 'print nodes' do
|
32
|
+
puts 'nodes'
|
33
|
+
end
|
34
|
+
|
35
|
+
op.summary_width = 22
|
36
|
+
|
37
|
+
if args.length > 1
|
38
|
+
puts op
|
39
|
+
exit
|
40
|
+
end
|
41
|
+
end.parse!
|
42
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
module Musk
|
2
|
+
class Lexer
|
3
|
+
module Helper
|
4
|
+
def is_alpha_numeric(char)
|
5
|
+
is_alpha(char) || is_digit(char)
|
6
|
+
end
|
7
|
+
|
8
|
+
def is_alpha(char)
|
9
|
+
('A' <= char && char <= 'Z') ||
|
10
|
+
('a' <= char && char <= 'z') ||
|
11
|
+
('_' == char)
|
12
|
+
end
|
13
|
+
|
14
|
+
def is_digit(char)
|
15
|
+
'0' <= char && char <= '9'
|
16
|
+
end
|
17
|
+
|
18
|
+
def is_bool(str)
|
19
|
+
str == 'true' || str == 'false'
|
20
|
+
end
|
21
|
+
|
22
|
+
def is_nil(str)
|
23
|
+
str == 'nil'
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,51 @@
|
|
1
|
+
module Musk
|
2
|
+
class Lexer
|
3
|
+
module Keywords
|
4
|
+
RESERVED_WORDS = {
|
5
|
+
:if => Token::If,
|
6
|
+
:else => Token::Else,
|
7
|
+
:switch => Token::Switch,
|
8
|
+
:case => Token::Case,
|
9
|
+
:default => Token::Default,
|
10
|
+
:for => Token::For,
|
11
|
+
:in => Token::In,
|
12
|
+
:loop => Token::Loop,
|
13
|
+
:break => Token::Break,
|
14
|
+
:continue => Token::Continue,
|
15
|
+
:class => Token::Class,
|
16
|
+
:extends => Token::Extends,
|
17
|
+
:super => Token::Super,
|
18
|
+
:self => Token::Self,
|
19
|
+
:get => Token::Get,
|
20
|
+
:set => Token::Set,
|
21
|
+
:fun => Token::Fun,
|
22
|
+
:return => Token::Return,
|
23
|
+
:let => Token::Let,
|
24
|
+
:mut => Token::Mut,
|
25
|
+
:and => Token::And,
|
26
|
+
:or => Token::Or,
|
27
|
+
:is => Token::Is,
|
28
|
+
:not => Token::Not,
|
29
|
+
:import => Token::Import,
|
30
|
+
:as => Token::As,
|
31
|
+
:throw => Token::Throw,
|
32
|
+
:try => Token::Try
|
33
|
+
}
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
# var tokens = [...]string{
|
39
|
+
# Int: "<int>",
|
40
|
+
# Float: "<float>",
|
41
|
+
# Identifier: "Identifier",
|
42
|
+
# String: "<string>",
|
43
|
+
# Rune: "<rune>",
|
44
|
+
# Bool: "<bool>",
|
45
|
+
# Nil: "nil",
|
46
|
+
# HashBang: "#!",
|
47
|
+
# Pipeline: "|>",
|
48
|
+
# Tilde: "~",
|
49
|
+
# Println: "println",
|
50
|
+
# Illegal: "Illegal",
|
51
|
+
# EOF: "EOF",
|
data/lib/musk/lexer.rb
ADDED
@@ -0,0 +1,309 @@
|
|
1
|
+
require 'musk/lexer/keywords'
|
2
|
+
require 'musk/lexer/helper'
|
3
|
+
|
4
|
+
module Musk
|
5
|
+
class Lexer
|
6
|
+
include Helper
|
7
|
+
include Keywords
|
8
|
+
|
9
|
+
attr_reader :tokens
|
10
|
+
|
11
|
+
def initialize(code)
|
12
|
+
@tokens = []
|
13
|
+
@input = code
|
14
|
+
@line = 1
|
15
|
+
@start = 0
|
16
|
+
@current = 0
|
17
|
+
end
|
18
|
+
|
19
|
+
def scan_tokens
|
20
|
+
until at_end?
|
21
|
+
@start = @current
|
22
|
+
scan_token
|
23
|
+
end
|
24
|
+
|
25
|
+
add_token(Token::EOF, '')
|
26
|
+
@tokens
|
27
|
+
end
|
28
|
+
|
29
|
+
def next_token
|
30
|
+
until at_end?
|
31
|
+
@start = @current
|
32
|
+
break if scan_token
|
33
|
+
end
|
34
|
+
|
35
|
+
@token_i = 0 if @token_i.nil?
|
36
|
+
@token_i += 1
|
37
|
+
|
38
|
+
if @token_i <= @tokens.size
|
39
|
+
@tokens.last
|
40
|
+
else
|
41
|
+
nil
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def scan_token
|
48
|
+
char = advance
|
49
|
+
|
50
|
+
return false if skip_whitespace(char)
|
51
|
+
return false if skip_comment(char)
|
52
|
+
|
53
|
+
single_token(char) ||
|
54
|
+
double_token(char) ||
|
55
|
+
triple_token(char) ||
|
56
|
+
string_token(char) ||
|
57
|
+
number_token(char) ||
|
58
|
+
identifier_token(char)
|
59
|
+
end
|
60
|
+
|
61
|
+
def skip_whitespace(char)
|
62
|
+
if char == "\n"
|
63
|
+
@line += 1
|
64
|
+
return true
|
65
|
+
end
|
66
|
+
|
67
|
+
char == "\r" ||
|
68
|
+
char == "\t" ||
|
69
|
+
char == ' '
|
70
|
+
end
|
71
|
+
|
72
|
+
def skip_comment(char)
|
73
|
+
if char == '/' && match?('/')
|
74
|
+
until peek == "\n" || at_end?
|
75
|
+
advance
|
76
|
+
end
|
77
|
+
|
78
|
+
true
|
79
|
+
else
|
80
|
+
false
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
def identifier_token(char)
|
85
|
+
if is_alpha(char)
|
86
|
+
while is_alpha_numeric(peek)
|
87
|
+
advance
|
88
|
+
end
|
89
|
+
|
90
|
+
lexeme = @input[@start...@current]
|
91
|
+
|
92
|
+
if is_bool(lexeme)
|
93
|
+
add_token_bool(lexeme)
|
94
|
+
elsif is_nil(lexeme)
|
95
|
+
add_token(Token::Nil, 'nil')
|
96
|
+
else
|
97
|
+
if type = RESERVED_WORDS[lexeme.to_sym]
|
98
|
+
add_token(type, lexeme)
|
99
|
+
else
|
100
|
+
add_token(Token::Identifier, lexeme)
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
true
|
105
|
+
else
|
106
|
+
add_token(Token::Illegal, nil, "unexpected '#{char}'")
|
107
|
+
false
|
108
|
+
end
|
109
|
+
end
|
110
|
+
|
111
|
+
def string_token(char)
|
112
|
+
if char == '"'
|
113
|
+
until peek == '"' || at_end?
|
114
|
+
if peek == "\n"
|
115
|
+
@line += 1
|
116
|
+
end
|
117
|
+
|
118
|
+
advance
|
119
|
+
end
|
120
|
+
|
121
|
+
if at_end?
|
122
|
+
add_token(Token::Illegal, nil, 'unterminated string')
|
123
|
+
return false
|
124
|
+
end
|
125
|
+
|
126
|
+
advance
|
127
|
+
add_token_string(@input[@start...@current])
|
128
|
+
|
129
|
+
true
|
130
|
+
else
|
131
|
+
false
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
def number_token(char)
|
136
|
+
if is_digit(char)
|
137
|
+
while is_digit(peek)
|
138
|
+
advance
|
139
|
+
end
|
140
|
+
|
141
|
+
if peek == '.' && is_digit(peek_next)
|
142
|
+
advance
|
143
|
+
|
144
|
+
while is_digit(peek)
|
145
|
+
advance
|
146
|
+
end
|
147
|
+
|
148
|
+
add_token_float(@input[@start...@current])
|
149
|
+
else
|
150
|
+
add_token_int(@input[@start...@current])
|
151
|
+
end
|
152
|
+
|
153
|
+
true
|
154
|
+
else
|
155
|
+
false
|
156
|
+
end
|
157
|
+
end
|
158
|
+
|
159
|
+
def single_token(char)
|
160
|
+
case char
|
161
|
+
when '+' then add_token(Token::Plus)
|
162
|
+
when '-' then add_token(Token::Minus)
|
163
|
+
when '*' then add_token(Token::Star)
|
164
|
+
when '/' then add_token(Token::Slash)
|
165
|
+
when '%' then add_token(Token::Modulo)
|
166
|
+
when ',' then add_token(Token::Comma)
|
167
|
+
when ':' then add_token(Token::Colon)
|
168
|
+
when ';' then add_token(Token::Semi)
|
169
|
+
when '.' then add_token(Token::Dot)
|
170
|
+
when '{' then add_token(Token::LeftBrace)
|
171
|
+
when '}' then add_token(Token::RightBrace)
|
172
|
+
when '[' then add_token(Token::LeftSquare)
|
173
|
+
when ']' then add_token(Token::RightSquare)
|
174
|
+
when '(' then add_token(Token::LeftParen)
|
175
|
+
when ')' then add_token(Token::RightParen)
|
176
|
+
else
|
177
|
+
return false
|
178
|
+
end
|
179
|
+
|
180
|
+
true
|
181
|
+
end
|
182
|
+
|
183
|
+
def double_token(char)
|
184
|
+
case char
|
185
|
+
when '!'
|
186
|
+
if match?('=')
|
187
|
+
add_token(Token::BangEq)
|
188
|
+
else
|
189
|
+
add_token(Token::Bang)
|
190
|
+
end
|
191
|
+
when '='
|
192
|
+
if match?('=')
|
193
|
+
add_token(Token::EqualEq)
|
194
|
+
else
|
195
|
+
add_token(Token::Equal)
|
196
|
+
end
|
197
|
+
when '>'
|
198
|
+
if match?('=')
|
199
|
+
add_token(Token::GreaterEq)
|
200
|
+
else
|
201
|
+
add_token(Token::Greater)
|
202
|
+
end
|
203
|
+
when '<'
|
204
|
+
if match?('=')
|
205
|
+
add_token(Token::LessEq)
|
206
|
+
else
|
207
|
+
add_token(Token::Less)
|
208
|
+
end
|
209
|
+
when '?'
|
210
|
+
if match?('.')
|
211
|
+
add_token(Token::QuestionDot)
|
212
|
+
else
|
213
|
+
add_token(Token::Question)
|
214
|
+
end
|
215
|
+
else
|
216
|
+
return false
|
217
|
+
end
|
218
|
+
|
219
|
+
true
|
220
|
+
end
|
221
|
+
|
222
|
+
def triple_token(char)
|
223
|
+
case char
|
224
|
+
when '&'
|
225
|
+
if match?('&')
|
226
|
+
if match?('=')
|
227
|
+
add_token(Token::Illegal)
|
228
|
+
else
|
229
|
+
add_token(Token::And)
|
230
|
+
end
|
231
|
+
else
|
232
|
+
add_token(Token::BitwiseAnd)
|
233
|
+
end
|
234
|
+
when '|'
|
235
|
+
if match?('|')
|
236
|
+
if match?('=')
|
237
|
+
add_token(Token::Illegal)
|
238
|
+
else
|
239
|
+
add_token(Token::Or)
|
240
|
+
end
|
241
|
+
else
|
242
|
+
if match?('>')
|
243
|
+
add_token(Token::Pipeline)
|
244
|
+
else
|
245
|
+
add_token(Token::BitwiseOr)
|
246
|
+
end
|
247
|
+
end
|
248
|
+
else
|
249
|
+
return false
|
250
|
+
end
|
251
|
+
|
252
|
+
true
|
253
|
+
end
|
254
|
+
|
255
|
+
def add_token_bool(lexeme)
|
256
|
+
value = lexeme.eql?('true')
|
257
|
+
add_token(Token::Bool, lexeme, value)
|
258
|
+
end
|
259
|
+
|
260
|
+
def add_token_string(lexeme)
|
261
|
+
value = lexeme[1, lexeme.length - 2]
|
262
|
+
add_token(Token::String, lexeme, value)
|
263
|
+
end
|
264
|
+
|
265
|
+
def add_token_float(lexeme)
|
266
|
+
add_token(Token::Float, lexeme, lexeme.to_f)
|
267
|
+
end
|
268
|
+
|
269
|
+
def add_token_int(lexeme)
|
270
|
+
add_token(Token::Int, lexeme, lexeme.to_i)
|
271
|
+
end
|
272
|
+
|
273
|
+
def add_token(type, lexeme = nil, literal = nil)
|
274
|
+
lexeme = @input[@start...@current] if lexeme.nil?
|
275
|
+
@tokens.push(Token.new(type, lexeme, literal, @line))
|
276
|
+
end
|
277
|
+
|
278
|
+
def match?(char)
|
279
|
+
return false if at_end?
|
280
|
+
return false if @input[@current] != char
|
281
|
+
|
282
|
+
@current += 1
|
283
|
+
true
|
284
|
+
end
|
285
|
+
|
286
|
+
def peek
|
287
|
+
return "\0" if at_end?
|
288
|
+
@input[@current]
|
289
|
+
end
|
290
|
+
|
291
|
+
def peek_next
|
292
|
+
if @current + 1 < @input.length
|
293
|
+
@input[@current + 1]
|
294
|
+
else
|
295
|
+
"\0"
|
296
|
+
end
|
297
|
+
end
|
298
|
+
|
299
|
+
def advance
|
300
|
+
char = @input[@current]
|
301
|
+
@current += 1
|
302
|
+
char
|
303
|
+
end
|
304
|
+
|
305
|
+
def at_end?
|
306
|
+
@current >= @input.length
|
307
|
+
end
|
308
|
+
end
|
309
|
+
end
|
data/lib/musk/nodes.rb
ADDED
@@ -0,0 +1,214 @@
|
|
1
|
+
module Musk
|
2
|
+
module Nodes
|
3
|
+
class Node
|
4
|
+
def accept(visitor)
|
5
|
+
raise NotImplementedError
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class Program < Node; end
|
10
|
+
class Expression < Node; end
|
11
|
+
class Statement < Node; end
|
12
|
+
class Declaration < Statement; end
|
13
|
+
|
14
|
+
class BlockStatement < Statement
|
15
|
+
attr_reader :statements
|
16
|
+
|
17
|
+
def initialize(statements:)
|
18
|
+
@statements = statements
|
19
|
+
end
|
20
|
+
|
21
|
+
def accept(visitor)
|
22
|
+
visitor.visit_block_stmt(self)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
class IfStatement < Statement
|
27
|
+
# // Condition Expression
|
28
|
+
# // Then Statement
|
29
|
+
# // Else Statement
|
30
|
+
end
|
31
|
+
|
32
|
+
class ForStatement < Statement
|
33
|
+
end
|
34
|
+
|
35
|
+
class LoopStatement < Statement
|
36
|
+
# // Condition Expression
|
37
|
+
# // Body Statement
|
38
|
+
end
|
39
|
+
|
40
|
+
class ContinueStatement < Statement
|
41
|
+
# Label Expression
|
42
|
+
end
|
43
|
+
|
44
|
+
class BreakStatement < Statement
|
45
|
+
# Label Expression
|
46
|
+
end
|
47
|
+
|
48
|
+
class ReturnStatement < Statement
|
49
|
+
attr_reader :keyword, :value
|
50
|
+
|
51
|
+
def initialize(keyword:, value:)
|
52
|
+
@keyword = keyword
|
53
|
+
@value = value
|
54
|
+
# Argument Expression
|
55
|
+
end
|
56
|
+
|
57
|
+
def accept(visitor)
|
58
|
+
visitor.visit_return_stmt(self)
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
class ExpressionStatement < Statement
|
63
|
+
attr_reader :expression
|
64
|
+
|
65
|
+
def initialize(expression:)
|
66
|
+
@expression = expression
|
67
|
+
end
|
68
|
+
|
69
|
+
def accept(visitor)
|
70
|
+
visitor.visit_expression_stmt(self)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
class ClassDeclaration < Declaration
|
75
|
+
# // Name token.Token
|
76
|
+
# // Methods []ClassMethod
|
77
|
+
# // SuperClass *VariableExpression
|
78
|
+
end
|
79
|
+
|
80
|
+
class VariableDeclaration < Declaration
|
81
|
+
# // Name token.Token
|
82
|
+
# // Initializer Expression
|
83
|
+
end
|
84
|
+
|
85
|
+
class FunctionDeclaration < Declaration
|
86
|
+
attr_reader :name, :params, :body
|
87
|
+
|
88
|
+
def initialize(name:, params:, body:)
|
89
|
+
@name = name
|
90
|
+
@params = params
|
91
|
+
@body = body
|
92
|
+
end
|
93
|
+
|
94
|
+
def accept(visitor)
|
95
|
+
visitor.visit_function_stmt(self)
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
class Function < Expression
|
100
|
+
attr_reader :params, :body
|
101
|
+
|
102
|
+
def initialize(params:, body:)
|
103
|
+
@params = params
|
104
|
+
@body = body
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
class Assignment < Expression
|
109
|
+
# Operator token.Token
|
110
|
+
# Right Expression
|
111
|
+
# Left Expression
|
112
|
+
# //> expr-assign
|
113
|
+
# static class Assign extends Expr {
|
114
|
+
# Assign(Token name, Expr value) {
|
115
|
+
# this.name = name;
|
116
|
+
# this.value = value;
|
117
|
+
# <R> R accept(Visitor<R> visitor) {
|
118
|
+
# return visitor.visitAssignExpr(this);
|
119
|
+
# final Token name;
|
120
|
+
# final Expr value;
|
121
|
+
end
|
122
|
+
|
123
|
+
# type Variable struct {
|
124
|
+
# Name string
|
125
|
+
# Expression
|
126
|
+
# }
|
127
|
+
|
128
|
+
# type Identifier struct {
|
129
|
+
# Name string
|
130
|
+
# Expression
|
131
|
+
# }
|
132
|
+
|
133
|
+
class Call < Expression
|
134
|
+
attr_reader :callee, :args
|
135
|
+
|
136
|
+
def initialize(callee:, args:)
|
137
|
+
@callee = callee
|
138
|
+
@args = args
|
139
|
+
# final Token paren;
|
140
|
+
# this.paren = paren;
|
141
|
+
end
|
142
|
+
|
143
|
+
def accept(visitor)
|
144
|
+
visitor.visit_call_expr(self)
|
145
|
+
end
|
146
|
+
end
|
147
|
+
|
148
|
+
class Logical < Expression
|
149
|
+
attr_reader :left, :operator, :right
|
150
|
+
|
151
|
+
def initialize(left:, operator:, right:)
|
152
|
+
@left = left
|
153
|
+
@operator = operator
|
154
|
+
@right = right
|
155
|
+
end
|
156
|
+
|
157
|
+
def accept(visitor)
|
158
|
+
visitor.visit_logical_expr(self)
|
159
|
+
end
|
160
|
+
end
|
161
|
+
|
162
|
+
class Grouping < Expression
|
163
|
+
attr_reader :expr
|
164
|
+
|
165
|
+
def initialize(expr:)
|
166
|
+
@expr = expr
|
167
|
+
end
|
168
|
+
|
169
|
+
def accept(visitor)
|
170
|
+
visitor.visit_grouping_expr(self)
|
171
|
+
end
|
172
|
+
end
|
173
|
+
|
174
|
+
class Binary < Expression
|
175
|
+
attr_reader :left, :operator, :right
|
176
|
+
|
177
|
+
def initialize(left:, operator:, right:)
|
178
|
+
@left = left
|
179
|
+
@operator = operator
|
180
|
+
@right = right
|
181
|
+
end
|
182
|
+
|
183
|
+
def accept(visitor)
|
184
|
+
visitor.visit_binary_expr(self)
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
class Unary < Expression
|
189
|
+
attr_reader :operator, :right
|
190
|
+
|
191
|
+
def initialize(operator:, right:)
|
192
|
+
@operator = operator
|
193
|
+
@right = right
|
194
|
+
end
|
195
|
+
|
196
|
+
def accept(visitor)
|
197
|
+
visitor.visit_unary_expr(self)
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
class Literal < Expression
|
202
|
+
attr_reader :type, :value
|
203
|
+
|
204
|
+
def initialize(type:, value:)
|
205
|
+
@type = type
|
206
|
+
@value = value
|
207
|
+
end
|
208
|
+
|
209
|
+
def accept(visitor)
|
210
|
+
visitor.visit_literal_expr(self)
|
211
|
+
end
|
212
|
+
end
|
213
|
+
end
|
214
|
+
end
|
data/lib/musk/parser.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
module Musk
|
2
|
+
class Parser
|
3
|
+
def initialize(tokens)
|
4
|
+
@tokens = tokens
|
5
|
+
@current = 0
|
6
|
+
end
|
7
|
+
|
8
|
+
def parse
|
9
|
+
statements = []
|
10
|
+
statements.push(declaration) until at_end?
|
11
|
+
# statements.add(statement());
|
12
|
+
statements
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
|
17
|
+
def declaration
|
18
|
+
begin
|
19
|
+
# if (match(CLASS)) return classDeclaration();
|
20
|
+
# if (match(FUN)) return function("function");
|
21
|
+
# if (match(VAR)) return varDeclaration();
|
22
|
+
statement
|
23
|
+
rescue ParseError
|
24
|
+
synchronize
|
25
|
+
nil
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def statement
|
30
|
+
return if_statement if match?(Token::If)
|
31
|
+
return for_statement if match?(Token::For)
|
32
|
+
return loop_statement if match?(Token::Loop)
|
33
|
+
return return_statement if match?(Token::Return)
|
34
|
+
|
35
|
+
# if (match(LEFT_BRACE)) return new Stmt.Block(block());
|
36
|
+
# return expressionStatement();
|
37
|
+
end
|
38
|
+
|
39
|
+
# private Stmt expressionStatement() {
|
40
|
+
# Expr expr = expression();
|
41
|
+
# consume(SEMICOLON, "Expect ';' after expression.");
|
42
|
+
# return new Stmt.Expression(expr);
|
43
|
+
# }
|
44
|
+
|
45
|
+
|
46
|
+
def synchronize
|
47
|
+
advance
|
48
|
+
|
49
|
+
until at_end?
|
50
|
+
return if previous.type == Token::Semi
|
51
|
+
return if peek.type == Token::Class
|
52
|
+
return if peek.type == Token::Fun
|
53
|
+
return if peek.type == Token::Return
|
54
|
+
return if peek.type == Token::Let
|
55
|
+
return if peek.type == Token::If
|
56
|
+
return if peek.type == Token::Switch
|
57
|
+
return if peek.type == Token::For
|
58
|
+
return if peek.type == Token::Loop
|
59
|
+
|
60
|
+
advance
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
|
65
|
+
def expression
|
66
|
+
assignment
|
67
|
+
end
|
68
|
+
|
69
|
+
def assignment
|
70
|
+
|
71
|
+
end
|
72
|
+
|
73
|
+
# private Expr unary() {
|
74
|
+
# if (match(BANG, MINUS)) {
|
75
|
+
# Token operator = previous();
|
76
|
+
# Expr right = unary();
|
77
|
+
# return new Expr.Unary(operator, right);
|
78
|
+
# }
|
79
|
+
# return call();
|
80
|
+
# }
|
81
|
+
|
82
|
+
def primary
|
83
|
+
return literal(previous) if match?(Token::Nil, Token::Bool)
|
84
|
+
return literal(previous) if match?(Token::Int, Token::Float)
|
85
|
+
return literal(previous) if match?(Token::String)
|
86
|
+
|
87
|
+
if match?(Token::Super)
|
88
|
+
# Token keyword = previous();
|
89
|
+
consume(Token::Dot, "expect '.' after 'super'")
|
90
|
+
method = consume(Token::Identifier, 'expect superclass method name')
|
91
|
+
return Nodes::Super.new()
|
92
|
+
# .(keyword, method);
|
93
|
+
# .{Method: tok.Value}
|
94
|
+
end
|
95
|
+
|
96
|
+
if match?(Token::Self)
|
97
|
+
return Nodes::Self.new(previous)
|
98
|
+
end
|
99
|
+
|
100
|
+
if match?(Token::Identifier)
|
101
|
+
# tok := p.previous()
|
102
|
+
# return ast.Variable{Name: tok.Value}
|
103
|
+
# return new Expr.Variable(previous());
|
104
|
+
end
|
105
|
+
|
106
|
+
if match?(Token::LeftParen)
|
107
|
+
exp = primary
|
108
|
+
# expr = expression();
|
109
|
+
consume(Token::RightParen, "expect ')' after expression")
|
110
|
+
return Nodes::Grouping.new(expr: exp)
|
111
|
+
end
|
112
|
+
|
113
|
+
# // panic(ParseError{token: p.peek(), message: "Expect expression"})
|
114
|
+
# panic("error")
|
115
|
+
# }
|
116
|
+
# throw error(peek(), "Expect expression.");
|
117
|
+
end
|
118
|
+
|
119
|
+
def literal(token)
|
120
|
+
Nodes::Literal.new(type: token.type, value: token.literal)
|
121
|
+
end
|
122
|
+
|
123
|
+
def consume(type, message)
|
124
|
+
return advance if check?(type)
|
125
|
+
error peek, message
|
126
|
+
raise ParseError
|
127
|
+
end
|
128
|
+
|
129
|
+
def error(tok, message)
|
130
|
+
if tok.type == Token::EOF
|
131
|
+
# report(token.line, " at end", message);
|
132
|
+
else
|
133
|
+
# report(token.line, " at '" + token.lexeme + "'", message);
|
134
|
+
end
|
135
|
+
end
|
136
|
+
|
137
|
+
def match?(*types)
|
138
|
+
types.each do |type|
|
139
|
+
if check?(type)
|
140
|
+
advance
|
141
|
+
return true
|
142
|
+
end
|
143
|
+
end
|
144
|
+
|
145
|
+
false
|
146
|
+
end
|
147
|
+
|
148
|
+
def check?(type)
|
149
|
+
return false if at_end?
|
150
|
+
peek.type == type
|
151
|
+
end
|
152
|
+
|
153
|
+
def advance
|
154
|
+
@current += 1 unless at_end?
|
155
|
+
previous
|
156
|
+
end
|
157
|
+
|
158
|
+
def previous
|
159
|
+
@tokens[@current - 1]
|
160
|
+
end
|
161
|
+
|
162
|
+
def peek
|
163
|
+
@tokens[@current]
|
164
|
+
end
|
165
|
+
|
166
|
+
def at_end?
|
167
|
+
peek.type == Token::EOF
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
data/lib/musk/token.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
module Musk
|
2
|
+
class Token
|
3
|
+
class << self
|
4
|
+
def token_type(*types)
|
5
|
+
types.each do |type|
|
6
|
+
const_set type, type.to_s
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_reader :type, :lexeme, :literal, :line
|
12
|
+
|
13
|
+
token_type :Plus, :Minus, :Star, :Slash, :Modulo,
|
14
|
+
:Comma, :Colon, :Semi, :Dot,
|
15
|
+
:LeftBrace, :RightBrace,
|
16
|
+
:LeftSquare, :RightSquare,
|
17
|
+
:LeftParen, :RightParen
|
18
|
+
|
19
|
+
token_type :Bang, :BangEq, :Equal, :EqualEq,
|
20
|
+
:Greater, :GreaterEq, :Less, :LessEq,
|
21
|
+
:BitwiseAnd, :BitwiseOr, :And, :Or,
|
22
|
+
:Question, :QuestionDot,
|
23
|
+
:Pipeline
|
24
|
+
|
25
|
+
token_type :Int, :Float, :String, :Char, :Bool, :Nil,
|
26
|
+
:Identifier, :Illegal, :EOF
|
27
|
+
|
28
|
+
token_type :If, :Else, :Switch, :Case, :Default,
|
29
|
+
:For, :In, :Loop, :Break, :Continue,
|
30
|
+
:Class, :Extends, :Super, :Self,
|
31
|
+
:Get, :Set, :Fun, :Return,
|
32
|
+
:Let, :Mut, :Is, :Not,
|
33
|
+
:Import, :As,
|
34
|
+
:Throw, :Try
|
35
|
+
|
36
|
+
def initialize(type, lexeme, literal = nil, line)
|
37
|
+
@type = type
|
38
|
+
@lexeme = lexeme
|
39
|
+
@literal = literal
|
40
|
+
@line = line
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_s
|
44
|
+
return @type if @literal.nil?
|
45
|
+
"#{@type} #{@lexeme} #{@literal}"
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/musk/types.rb
ADDED
data/lib/musk/version.rb
ADDED
data/lib/musk.rb
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'musk/version'
|
2
|
+
require 'musk/token'
|
3
|
+
require 'musk/lexer'
|
4
|
+
require 'musk/nodes'
|
5
|
+
require 'musk/parser'
|
6
|
+
|
7
|
+
module Musk
|
8
|
+
class ParseError < RuntimeError; end
|
9
|
+
|
10
|
+
module_function
|
11
|
+
|
12
|
+
def run_prompt
|
13
|
+
require 'pastel'
|
14
|
+
require 'readline'
|
15
|
+
|
16
|
+
stty_save = `stty -g`.chomp
|
17
|
+
|
18
|
+
begin
|
19
|
+
while buf = Readline.readline('> ', true)
|
20
|
+
break if buf.nil?
|
21
|
+
break if buf.strip == 'exit'
|
22
|
+
|
23
|
+
print run(buf), "\n"
|
24
|
+
# run(line); //> reset-had-error
|
25
|
+
# hadError = false;
|
26
|
+
end
|
27
|
+
rescue Interrupt
|
28
|
+
system 'stty', stty_save
|
29
|
+
ensure
|
30
|
+
puts
|
31
|
+
exit
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
def run_file(path)
|
36
|
+
if File.exist?(path) && File.file?(path)
|
37
|
+
puts 'fiasd'
|
38
|
+
else
|
39
|
+
warn "No such file #{path}"
|
40
|
+
end
|
41
|
+
|
42
|
+
# run(File.read(path))
|
43
|
+
# # if (hadError) System.exit(65);
|
44
|
+
# # if (hadRuntimeError) System.exit(70);
|
45
|
+
end
|
46
|
+
|
47
|
+
def run(code)
|
48
|
+
lexer = Lexer.new(code)
|
49
|
+
tokens = lexer.scan_tokens
|
50
|
+
tokens.each { |token| puts token }
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# ["And",
|
55
|
+
# "Class",
|
56
|
+
# "Else",
|
57
|
+
# "False",
|
58
|
+
# "Fun",
|
59
|
+
# "For",
|
60
|
+
# "If",
|
61
|
+
# "Nil",
|
62
|
+
# "Or",
|
63
|
+
# "Print",
|
64
|
+
# "Return",
|
65
|
+
# "Super",
|
66
|
+
# "This",
|
67
|
+
# "True",
|
68
|
+
# "Var",
|
69
|
+
# "While"]
|
70
|
+
# Int
|
71
|
+
# Float
|
72
|
+
# Bool
|
73
|
+
# String
|
74
|
+
# Char
|
75
|
+
# Unit
|
76
|
+
# Tuple
|
77
|
+
# List
|
78
|
+
# Array
|
79
|
+
# Function
|
data/lib/mvsk.rb
CHANGED
@@ -1,2 +1 @@
|
|
1
|
-
|
2
|
-
end
|
1
|
+
# https://elm-lang.org/docs/syntax
|
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: mvsk
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.0.
|
4
|
+
version: 1.0.2
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- razeos at tossdev
|
@@ -9,13 +9,38 @@ autorequire:
|
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
11
|
date: 2023-07-18 00:00:00.000000000 Z
|
12
|
-
dependencies:
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: pastel
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: 0.8.0
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: 0.8.0
|
13
27
|
description:
|
14
28
|
email:
|
15
|
-
executables:
|
29
|
+
executables:
|
30
|
+
- mvsk
|
16
31
|
extensions: []
|
17
32
|
extra_rdoc_files: []
|
18
33
|
files:
|
34
|
+
- bin/mvsk
|
35
|
+
- lib/musk.rb
|
36
|
+
- lib/musk/lexer.rb
|
37
|
+
- lib/musk/lexer/helper.rb
|
38
|
+
- lib/musk/lexer/keywords.rb
|
39
|
+
- lib/musk/nodes.rb
|
40
|
+
- lib/musk/parser.rb
|
41
|
+
- lib/musk/token.rb
|
42
|
+
- lib/musk/types.rb
|
43
|
+
- lib/musk/version.rb
|
19
44
|
- lib/mvsk.rb
|
20
45
|
homepage:
|
21
46
|
licenses:
|
@@ -39,5 +64,5 @@ requirements: []
|
|
39
64
|
rubygems_version: 3.1.2
|
40
65
|
signing_key:
|
41
66
|
specification_version: 4
|
42
|
-
summary:
|
67
|
+
summary: A simple programming language
|
43
68
|
test_files: []
|