NCPrePatcher 0.2.0-arm64-darwin-23
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.
- checksums.yaml +7 -0
- data/LICENSE.txt +674 -0
- data/README.md +66 -0
- data/example/README.md +3 -0
- data/example/disasm.rb +34 -0
- data/exe/ncpp +4 -0
- data/lib/ncpp/commands.rb +903 -0
- data/lib/ncpp/interpreter.rb +919 -0
- data/lib/ncpp/parser.rb +249 -0
- data/lib/ncpp/types.rb +68 -0
- data/lib/ncpp/utils.rb +700 -0
- data/lib/ncpp/version.rb +4 -0
- data/lib/ncpp.rb +478 -0
- data/lib/nitro/nitro.dylib +0 -0
- data/lib/nitro/nitro.rb +440 -0
- data/lib/unarm/unarm.dylib +0 -0
- data/lib/unarm/unarm.rb +836 -0
- metadata +91 -0
data/lib/ncpp/parser.rb
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
require 'parslet'
|
|
2
|
+
|
|
3
|
+
module NCPP
|
|
4
|
+
|
|
5
|
+
#
|
|
6
|
+
# Parses raw text into a tree
|
|
7
|
+
#
|
|
8
|
+
class Parser < Parslet::Parser
|
|
9
|
+
|
|
10
|
+
def initialize(cmd_prefix: 'ncpp_')
|
|
11
|
+
@COMMAND_PREFIX = cmd_prefix
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def parse(str, root: :line)
|
|
15
|
+
send(root).parse(str)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
root :line
|
|
19
|
+
|
|
20
|
+
rule :line do
|
|
21
|
+
space? >>
|
|
22
|
+
((str('//')|str('/*')|str('"')).absent? >> expression >> (any.repeat)).maybe >>
|
|
23
|
+
any.repeat
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
rule :expression do
|
|
27
|
+
ternary_operation | binary_operation | unary_operation | body
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
rule :body do
|
|
31
|
+
float | integer | boolean | null | string | command | block | array | variable
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
rule :binary_operation do
|
|
35
|
+
infix_expression(spaced(unary_operation|primary),
|
|
36
|
+
[match['*/%'], 11, :left], # mul, div, modulo
|
|
37
|
+
[match['+-'], 10, :left], # add, sub
|
|
38
|
+
[str('<<')|str('>>'), 9, :left], # bitwise left/right shift
|
|
39
|
+
[str('<=>'), 8, :left], # three-way comparison
|
|
40
|
+
[match['><'] >> str('=').maybe,
|
|
41
|
+
7, :left], # relational >, ≥, <, ≤
|
|
42
|
+
[str('==')|str('!='), 6, :left], # relational =, ≠
|
|
43
|
+
[str('&&'), 2, :left], # logical AND
|
|
44
|
+
[str('||'), 1, :left], # logical OR
|
|
45
|
+
[str('&'), 5, :left], # bitwise AND
|
|
46
|
+
[str('^'), 4, :left], # bitwise XOR
|
|
47
|
+
[str('|'), 3, :left] # bitwise OR
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
rule :ternary_operation do
|
|
52
|
+
(binary_operation|unary_operation|primary).as(:cond) >> spaced(str('?')) >> primary.as(:e1) >> spaced(str(':')) >> primary.as(:e2)
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
rule :unary_operation do
|
|
56
|
+
match['!~\\-+*'].as(:op) >> primary.as(:e)
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
rule :primary do
|
|
60
|
+
chained_command | command | variable | group | float | integer
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
rule(:group) { lparen >> (expression.as(:group) | str('').as(:empty_group)) >> rparen >> lbrace.absent? }
|
|
64
|
+
|
|
65
|
+
rule(:identifier) { digits.absent? >> match['A-Za-z0-9_'].repeat(1) }
|
|
66
|
+
|
|
67
|
+
rule :command do
|
|
68
|
+
str(@COMMAND_PREFIX).maybe >> identifier.as(:cmd_name) >>
|
|
69
|
+
lparen >>
|
|
70
|
+
(expression >> (comma >> expression).repeat).repeat.as(:args) >>
|
|
71
|
+
rparen.as(:__last_char__) >> subscript.maybe
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
rule(:boolean) { (str('true') | str('false')).as(:bool) }
|
|
75
|
+
|
|
76
|
+
rule(:null) { (str('nil') | str('NULL')).as(:nil) }
|
|
77
|
+
|
|
78
|
+
rule :variable do
|
|
79
|
+
str(@COMMAND_PREFIX).maybe >> identifier.as(:var_name) >> lparen.absent? >> subscript.maybe
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
rule :block do
|
|
83
|
+
block_args.maybe >> lbrace >> expression.repeat.as(:block_body) >> rbrace >> subscript.maybe
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
rule :block_args do
|
|
87
|
+
lparen >>
|
|
88
|
+
(identifier >> (comma >> identifier).repeat).as(:block_args).maybe >>
|
|
89
|
+
rparen
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
# rule :block_sequence do
|
|
93
|
+
# block_args.maybe >> lbrace >>
|
|
94
|
+
# (block >> (comma >> block).repeat).as(:block_sequence) >>
|
|
95
|
+
# rbrace >> subscript.maybe
|
|
96
|
+
# end
|
|
97
|
+
|
|
98
|
+
rule :array do
|
|
99
|
+
lbracket >>
|
|
100
|
+
(expression >> (comma >> expression).repeat).maybe.as(:array) >>
|
|
101
|
+
rbracket >> subscript.maybe
|
|
102
|
+
end
|
|
103
|
+
|
|
104
|
+
rule :chained_command do
|
|
105
|
+
(command|boolean|null|variable|group|block|array|float|integer|string).as(:base) >>
|
|
106
|
+
(str('.') >> command.as(:next)).repeat.as(:chain) >> subscript.maybe
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
rule :subscript do
|
|
110
|
+
lbracket >> expression.as(:subscript_idx) >> rbracket
|
|
111
|
+
end
|
|
112
|
+
|
|
113
|
+
rule(:space) { match['\s'].repeat(1) }
|
|
114
|
+
rule(:space?) { space.maybe }
|
|
115
|
+
|
|
116
|
+
def spaced(atom)
|
|
117
|
+
space? >> atom >> space?
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
rule(:comma) { spaced(str(',')) }
|
|
121
|
+
rule(:lparen) { spaced(str('(')) }
|
|
122
|
+
rule(:rparen) { spaced(str(')')) }
|
|
123
|
+
rule(:lbrace) { spaced(str('{')) }
|
|
124
|
+
rule(:rbrace) { spaced(str('}')) }
|
|
125
|
+
rule(:lbracket) { spaced(str('[')) }
|
|
126
|
+
rule(:rbracket) { spaced(str(']')) }
|
|
127
|
+
rule(:newline) { str("\n") >> str("\r").maybe }
|
|
128
|
+
rule(:eol) { str("\n") | any.absent? }
|
|
129
|
+
rule(:digit) { match['0-9'] }
|
|
130
|
+
rule(:digits) { digit.repeat(1) }
|
|
131
|
+
rule(:hex_digit) { match['0-9a-fA-F'] }
|
|
132
|
+
rule(:hex_digits) { hex_digit.repeat(1) }
|
|
133
|
+
rule(:bin_digit) { match['0-1'] }
|
|
134
|
+
rule(:bin_digits) { bin_digit.repeat(1) }
|
|
135
|
+
|
|
136
|
+
rule :float do
|
|
137
|
+
(digits.maybe >>
|
|
138
|
+
(
|
|
139
|
+
(
|
|
140
|
+
str('.')|str('e')) >>
|
|
141
|
+
match['+-'].maybe >> digits
|
|
142
|
+
) >>
|
|
143
|
+
(
|
|
144
|
+
str('e') >>
|
|
145
|
+
match['+-'].maybe >>
|
|
146
|
+
digits
|
|
147
|
+
).maybe
|
|
148
|
+
).as(:float) >> space?
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
rule :integer do
|
|
152
|
+
(
|
|
153
|
+
(
|
|
154
|
+
(str('0x') >> hex_digits) |
|
|
155
|
+
(str('0b') >> bin_digits) |
|
|
156
|
+
digits)
|
|
157
|
+
).as(:integer) >> space?
|
|
158
|
+
end
|
|
159
|
+
|
|
160
|
+
rule :string do
|
|
161
|
+
(str('"') >> (
|
|
162
|
+
str('\\') >> any | str('"').absent? >> any
|
|
163
|
+
).repeat.as(:string) >> str('"')) |
|
|
164
|
+
(str("'") >> (
|
|
165
|
+
str('\\') >> any | str("'").absent? >> any
|
|
166
|
+
).repeat.as(:string) >> str("'")) >> subscript.maybe
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
end
|
|
170
|
+
|
|
171
|
+
#
|
|
172
|
+
# Transforms parsed trees to ASTs
|
|
173
|
+
#
|
|
174
|
+
class Transformer < Parslet::Transform
|
|
175
|
+
rule(integer: simple(:x)) { Integer(x) }
|
|
176
|
+
rule(float: simple(:x)) { Float(x) }
|
|
177
|
+
rule(string: simple(:s)) { String(s) }
|
|
178
|
+
rule(string: sequence(:s)) { '' }
|
|
179
|
+
|
|
180
|
+
rule(array: subtree(:a)) do
|
|
181
|
+
{ array:
|
|
182
|
+
case a
|
|
183
|
+
when nil then []
|
|
184
|
+
when Array then a
|
|
185
|
+
else [a]
|
|
186
|
+
end
|
|
187
|
+
}
|
|
188
|
+
end
|
|
189
|
+
|
|
190
|
+
rule(group: subtree(:g)) { g }
|
|
191
|
+
rule(empty_group: simple(:g)) { nil }
|
|
192
|
+
|
|
193
|
+
rule(l: subtree(:lhs), o: simple(:op), r: subtree(:rhs)) do
|
|
194
|
+
{ infix: true, lhs: lhs, op: op.to_s, rhs: rhs }
|
|
195
|
+
end
|
|
196
|
+
|
|
197
|
+
rule(cond: subtree(:cond), e1: subtree(:e1), e2: subtree(:e2)) do
|
|
198
|
+
{ cond: cond, e1: e1, e2: e2 }
|
|
199
|
+
end
|
|
200
|
+
|
|
201
|
+
rule(op: simple(:op), e: subtree(:e)) do
|
|
202
|
+
{ op: op.to_s, e: e }
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
rule(cmd_name: simple(:n), args: subtree(:a)) do
|
|
206
|
+
args =
|
|
207
|
+
case a
|
|
208
|
+
when nil then []
|
|
209
|
+
when Array then a
|
|
210
|
+
else [a]
|
|
211
|
+
end
|
|
212
|
+
|
|
213
|
+
{ cmd_name: n.to_s, args: args }
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
rule(base: subtree(:b), chain: sequence(:cs)) do
|
|
217
|
+
{ base: b, chain: cs }
|
|
218
|
+
end
|
|
219
|
+
|
|
220
|
+
rule(block_body: subtree(:exprs)) do
|
|
221
|
+
{ block:
|
|
222
|
+
case exprs
|
|
223
|
+
when nil
|
|
224
|
+
[]
|
|
225
|
+
when Array
|
|
226
|
+
exprs.compact
|
|
227
|
+
else
|
|
228
|
+
[exprs]
|
|
229
|
+
end
|
|
230
|
+
}
|
|
231
|
+
end
|
|
232
|
+
|
|
233
|
+
rule(block_args: simple(:args), block_body: subtree(:exprs)) do
|
|
234
|
+
{ block:
|
|
235
|
+
case exprs
|
|
236
|
+
when nil
|
|
237
|
+
[]
|
|
238
|
+
when Array
|
|
239
|
+
exprs.compact
|
|
240
|
+
else
|
|
241
|
+
[exprs]
|
|
242
|
+
end,
|
|
243
|
+
args: args.to_s.split(',').map(&:strip)
|
|
244
|
+
}
|
|
245
|
+
end
|
|
246
|
+
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
end
|
data/lib/ncpp/types.rb
ADDED
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
require_relative 'utils.rb'
|
|
2
|
+
|
|
3
|
+
module NCPP
|
|
4
|
+
|
|
5
|
+
Block = Struct.new(:ast, :argns, :interpreter, :subs, :name) do
|
|
6
|
+
def eval(args)
|
|
7
|
+
result = nil
|
|
8
|
+
ast.each do |node|
|
|
9
|
+
if subs.nil?
|
|
10
|
+
result = interpreter.eval_expr(node, args.empty? || argns.nil? ? nil : Hash[argns.zip(args)])
|
|
11
|
+
else
|
|
12
|
+
result = interpreter.eval_expr(node, args.empty? || argns.nil? ? subs : subs.merge(Hash[argns.zip(args)]))
|
|
13
|
+
end
|
|
14
|
+
end
|
|
15
|
+
result
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def call(*args) = eval(args)
|
|
19
|
+
|
|
20
|
+
# if no_cache.nil? && pure?
|
|
21
|
+
# @cache ||= {}
|
|
22
|
+
# return @cache[args] if @cache.key?(args)
|
|
23
|
+
# result = eval(args)
|
|
24
|
+
# @cache[args] = result
|
|
25
|
+
# return result
|
|
26
|
+
# else
|
|
27
|
+
# return eval(args)
|
|
28
|
+
# end
|
|
29
|
+
|
|
30
|
+
def return_type = Object
|
|
31
|
+
|
|
32
|
+
def arg_names = argns
|
|
33
|
+
|
|
34
|
+
def pure?
|
|
35
|
+
@pure unless @pure.nil?
|
|
36
|
+
@pure = !ast.any? { |n| interpreter.node_impure?(n, name) }
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
# The following are not yet (and may never be) implemented
|
|
41
|
+
|
|
42
|
+
Boolean = Struct.new(:truthy) do
|
|
43
|
+
def true? = truthy
|
|
44
|
+
def false? = !truthy
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
class CodeLoc
|
|
48
|
+
attr_reader :addr, :ov, :code_bin
|
|
49
|
+
|
|
50
|
+
def initialize(loc, ov = nil)
|
|
51
|
+
@addr, @ov, @code_bin = Utils::resolve_code_loc(loc, ov)
|
|
52
|
+
end
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
class AsmFunc
|
|
56
|
+
attr_reader :instructions, :labels, :literal_pool
|
|
57
|
+
|
|
58
|
+
def initialize(instructions, labels, literal_pool)
|
|
59
|
+
@instructions = instructions
|
|
60
|
+
@labels = labels
|
|
61
|
+
@literal_pool = literal_pool
|
|
62
|
+
end
|
|
63
|
+
|
|
64
|
+
def to_s(reloc: true)
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
|
|
68
|
+
end
|