taoism 1.0.1 → 1.0.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,278 @@
1
+ module Taoism
2
+ class Lexer
3
+ KEYWORDS = {
4
+ 'if' => TokenType::IF,
5
+ 'else' => TokenType::ELSE,
6
+ 'loop' => TokenType::LOOP,
7
+ 'leave' => TokenType::LEAVE,
8
+ 'switch' => TokenType::SWITCH,
9
+ 'package' => TokenType::PACKAGE,
10
+ 'import' => TokenType::IMPORT,
11
+ 'const' => TokenType::CONST,
12
+ 'data' => TokenType::DATA,
13
+ 'fun' => TokenType::FUN,
14
+ 'let' => TokenType::LET,
15
+ 'mut' => TokenType::MUT,
16
+ 'try' => TokenType::TRY,
17
+ 'return' => TokenType::RETURN,
18
+ 'and' => TokenType::AND,
19
+ 'or' => TokenType::OR,
20
+ 'not' => TokenType::NOT,
21
+ }.freeze
22
+
23
+ PUNCTUATION = {
24
+ '{' => TokenType::LBRACE,
25
+ '}' => TokenType::RBRACE,
26
+ '[' => TokenType::LSQUARE,
27
+ ']' => TokenType::RSQUARE,
28
+ '(' => TokenType::LPAREN,
29
+ ')' => TokenType::RPAREN,
30
+ ',' => TokenType::COMMA,
31
+ ':' => TokenType::COLON,
32
+ ';' => TokenType::SEMI,
33
+ '.' => TokenType::DOT,
34
+ }.freeze
35
+
36
+ attr_reader :errors
37
+
38
+ def initialize(source)
39
+ @source = source
40
+ @offset = 0
41
+ @line = 1
42
+ @col = 0
43
+ @errors = ErrorHandler.new
44
+ end
45
+
46
+ def next_token
47
+ loop do
48
+ return eof_token if at_end?
49
+ @start = pos
50
+
51
+ char = advance
52
+ next if whitespace?(char)
53
+
54
+ if char == '/' && peek == '/'
55
+ skip_line_comment
56
+ next
57
+ end
58
+
59
+ return scan_token(char)
60
+ end
61
+ end
62
+
63
+ def skip_line_comment
64
+ advance
65
+ until at_end? || peek == "\n"
66
+ advance
67
+ end
68
+ end
69
+
70
+ def scan_token(char)
71
+ type = PUNCTUATION[char]
72
+ return make_token(type) if type
73
+
74
+ case char
75
+ when '+' then make_token(TokenType::PLUS)
76
+ when '-'
77
+ match('>') ? make_token(TokenType::ARROW) : make_token(TokenType::MINUS)
78
+ when '*' then make_token(TokenType::STAR)
79
+ when '/' then make_token(TokenType::SLASH)
80
+ when '%' then make_token(TokenType::MODULO)
81
+ when '>'
82
+ match('=') ? make_token(TokenType::GREATEREQ) : make_token(TokenType::GREATER)
83
+ when '<'
84
+ match('=') ? make_token(TokenType::LESSEQ) : make_token(TokenType::LESS)
85
+ when '='
86
+ if match('=')
87
+ make_token(TokenType::EQUALEQ)
88
+ elsif match('>')
89
+ make_token(TokenType::EQARROW)
90
+ else
91
+ make_token(TokenType::EQUAL)
92
+ end
93
+ when '!'
94
+ match('=') ? make_token(TokenType::BANGEQ) : make_token(TokenType::BANG)
95
+ when '&'
96
+ match('&') ? make_token(TokenType::AND) : make_token(TokenType::ILLEGAL)
97
+ when '|'
98
+ if match('>')
99
+ make_token(TokenType::PIPE)
100
+ elsif match('|')
101
+ make_token(TokenType::OR)
102
+ else
103
+ make_token(TokenType::ILLEGAL)
104
+ end
105
+ when '?'
106
+ match('.') ? make_token(TokenType::QDOT) : make_token(TokenType::ILLEGAL)
107
+ when '0'..'9' then number_token
108
+ when '"' then string_token
109
+ else
110
+ alpha?(char) ? identifier_token : make_token(TokenType::ILLEGAL)
111
+ end
112
+ end
113
+
114
+ def identifier_token
115
+ while alpha_numeric?(peek)
116
+ advance
117
+ end
118
+
119
+ str = lexeme
120
+
121
+ if bool_token?(str)
122
+ make_token(TokenType::BOOL)
123
+ elsif none_token?(str)
124
+ make_token(TokenType::NONE)
125
+ else
126
+ if type = KEYWORDS[str]
127
+ make_token(type)
128
+ else
129
+ make_token(TokenType::IDENTIFIER)
130
+ end
131
+ end
132
+ end
133
+
134
+ def string_token
135
+ loop do
136
+ if at_end?
137
+ @errors.add(:unterminated_string, "unterminated string", pos)
138
+ return make_token(TokenType::ILLEGAL, "")
139
+ end
140
+
141
+ case peek
142
+ when '"'
143
+ advance
144
+ return make_token(TokenType::STRING)
145
+ when "\n"
146
+ @errors.add(:unterminated_string, "unterminated string", pos)
147
+ return make_token(TokenType::ILLEGAL, "")
148
+ when '\\'
149
+ advance
150
+ if peek == '('
151
+ advance
152
+ while alpha_numeric?(peek)
153
+ advance
154
+ end
155
+
156
+ @errors.add(:bad_interpolation, "expected ')'", pos) unless peek == ')'
157
+ advance if peek == ')'
158
+
159
+ else
160
+ advance unless at_end?
161
+ end
162
+ else
163
+ advance
164
+ end
165
+ end
166
+ end
167
+
168
+ def number_token
169
+ type = TokenType::INT
170
+
171
+ while digit?(peek) || peek == '_'
172
+ advance
173
+ end
174
+
175
+ if peek == '.' && digit?(peek_next)
176
+ type = TokenType::FLOAT
177
+ advance
178
+
179
+ while digit?(peek) || peek == '_'
180
+ advance
181
+ end
182
+ end
183
+
184
+ make_token(type)
185
+ end
186
+
187
+ def make_token(type, text = lexeme)
188
+ Token.new(type, text, nil, @start, pos)
189
+ end
190
+
191
+ def eof_token
192
+ Token.new(TokenType::EOF, "", nil, @start, pos)
193
+ end
194
+
195
+ def lexeme
196
+ @source[@start.offset...@offset]
197
+ end
198
+
199
+ def pos
200
+ Token::Position.new(@offset, @line, @col)
201
+ end
202
+
203
+ def match(expected)
204
+ return false if peek != expected
205
+ advance
206
+ true
207
+ end
208
+
209
+ def peek
210
+ return "\0" if at_end?
211
+ @source[@offset]
212
+ end
213
+
214
+ def peek_next
215
+ if @offset.next < @source.length
216
+ @source[@offset + 1]
217
+ else
218
+ "\0"
219
+ end
220
+ end
221
+
222
+ def advance
223
+ char = @source[@offset]
224
+ @offset += 1
225
+ @col += 1
226
+ char
227
+ end
228
+
229
+ def at_end?
230
+ @offset >= @source.length
231
+ end
232
+
233
+ def bool_token?(str)
234
+ str == 'True' || str == 'False'
235
+ end
236
+
237
+ def none_token?(str)
238
+ str == 'None'
239
+ end
240
+
241
+ def whitespace?(char)
242
+ case char
243
+ when ' ', "\t", "\r" then true
244
+ when "\n"
245
+ @line += 1
246
+ @col = 0
247
+ true
248
+ else false
249
+ end
250
+ end
251
+
252
+ def alpha_numeric?(char)
253
+ alpha?(char) || digit?(char)
254
+ end
255
+
256
+ def alpha?(char)
257
+ ('A' <= char && char <= 'Z') ||
258
+ ('a' <= char && char <= 'z') ||
259
+ char == '_'
260
+ end
261
+
262
+ def digit?(char)
263
+ '0' <= char && char <= '9'
264
+ end
265
+
266
+ class ErrorHandler
267
+ attr_reader :errors
268
+
269
+ def initialize
270
+ @errors = []
271
+ end
272
+
273
+ def add(code, message = "", pos = nil)
274
+ @errors << LexicalError.new(code, message, pos)
275
+ end
276
+ end
277
+ end
278
+ end
@@ -0,0 +1,54 @@
1
+ module Taoism
2
+ module Nodes
3
+ # ── Top Level ──
4
+ Program = Struct.new(:stmts, :start, :end)
5
+
6
+ # ── Statements ──
7
+ Let = Struct.new(:name, :value, :mutable, :start, :end)
8
+ LetPair = Struct.new(:first, :second, :expr, :start, :end)
9
+ Assign = Struct.new(:target, :value, :start, :end)
10
+ Const = Struct.new(:name, :value, :start, :end)
11
+ Import = Struct.new(:path, :start, :end)
12
+ Package = Struct.new(:name, :start, :end)
13
+ Return = Struct.new(:value, :start, :end)
14
+ Leave = Struct.new(:start, :end)
15
+ ExprStmt = Struct.new(:expr, :start, :end)
16
+
17
+ # ── Function ──
18
+ FunDef = Struct.new(:name, :params, :body, :receiver, :start, :end)
19
+ Receiver = Struct.new(:param_name, :type_name, :start, :end)
20
+
21
+ # ── Data ──
22
+ DataDef = Struct.new(:name, :fields, :start, :end)
23
+ Field = Struct.new(:name, :default, :start, :end)
24
+
25
+ # ── Block ──
26
+ Block = Struct.new(:stmts, :start, :end)
27
+
28
+ # ── Expressions ──
29
+ BinaryOp = Struct.new(:left, :op, :right, :start, :end)
30
+ UnaryOp = Struct.new(:op, :operand, :start, :end)
31
+ Call = Struct.new(:callee, :args, :start, :end)
32
+ Index = Struct.new(:target, :index, :start, :end)
33
+ DotExpr = Struct.new(:target, :name, :start, :end)
34
+ SafeDot = Struct.new(:target, :name, :start, :end)
35
+ Identifier = Struct.new(:name, :start, :end)
36
+
37
+ # ── Literals ──
38
+ IntLit = Struct.new(:value, :start, :end)
39
+ FloatLit = Struct.new(:value, :start, :end)
40
+ StringLit = Struct.new(:value, :start, :end)
41
+ BoolLit = Struct.new(:value, :start, :end)
42
+ NoneLit = Struct.new(:start, :end)
43
+ ListLit = Struct.new(:elements, :start, :end)
44
+
45
+ # ── Special Forms ──
46
+ Try = Struct.new(:call, :start, :end)
47
+
48
+ # ── Control Flow ──
49
+ If = Struct.new(:cond, :then_body, :else_body, :start, :end)
50
+ Loop = Struct.new(:body, :start, :end)
51
+ Switch = Struct.new(:value, :arms, :else_body, :start, :end)
52
+ SwitchArm = Struct.new(:pattern, :value, :start, :end)
53
+ end
54
+ end