Mirror of CollapseOS
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

332 lines
6.8KB

  1. ; *** Requirements ***
  2. ; ari
  3. ;
  4. ; *** Defines ***
  5. ;
  6. ; EXPR_PARSE: routine to call to parse literals or symbols that are part of
  7. ; the expression. Routine's signature:
  8. ; String in (HL), returns its parsed value to DE. Z for success.
  9. ;
  10. ; *** Code ***
  11. ;
  12. ; Parse expression in string at (HL) and returns the result in DE.
  13. ; This routine needs to be able to mutate (HL), but it takes care of restoring
  14. ; the string to its original value before returning.
  15. ; Sets Z on success, unset on error.
  16. parseExpr:
  17. push iy
  18. push ix
  19. push hl
  20. call _parseAddSubst
  21. pop hl
  22. pop ix
  23. pop iy
  24. ret
  25. ; *** Op signature ***
  26. ; The signature of "operators routines" (.plus, .mult, etc) below is this:
  27. ; Combine HL and DE with an operator (+, -, *, etc) and put the result in DE.
  28. ; Destroys HL and A. Never fails. Yes, that's a problem for division by zero.
  29. ; Don't divide by zero. All other registers are protected.
  30. ; Given a running result in DE, a rest-of-expression in (HL), a parse routine
  31. ; in IY and an apply "operator routine" in IX, (HL/DE --> DE)
  32. ; With that, parse the rest of (HL) and apply the operation on it, then place
  33. ; HL at the end of the parsed string, with A containing the last char of it,
  34. ; which can be either an operator or a null char.
  35. ; Z for success.
  36. ;
  37. _parseApply:
  38. push de ; --> lvl 1, left result
  39. push ix ; --> lvl 2, routine to apply
  40. inc hl ; after op char
  41. call callIY ; --> DE
  42. pop ix ; <-- lvl 2, routine to apply
  43. ; Here we do some stack kung fu. We have, in HL, a string pointer we
  44. ; want to keep. We have, in (SP), our left result we want to use.
  45. ex (sp), hl ; <-> lvl 1
  46. jr nz, .end
  47. push af ; --> lvl 2, save ending operator
  48. call callIX
  49. pop af ; <-- lvl 2, restore operator.
  50. .end:
  51. pop hl ; <-- lvl 1, restore str pointer
  52. ret
  53. ; Unless there's an error, this routine completely resolves any valid expression
  54. ; from (HL) and puts the result in DE.
  55. ; Destroys HL
  56. ; Z for success.
  57. _parseAddSubst:
  58. call _parseMultDiv
  59. ret nz
  60. .loop:
  61. ; do we have an operator?
  62. or a
  63. ret z ; null char, we're done
  64. ; We have an operator. Resolve the rest of the expr then apply it.
  65. ld ix, .plus
  66. cp '+'
  67. jr z, .found
  68. ld ix, .minus
  69. cp '-'
  70. ret nz ; unknown char, error
  71. .found:
  72. ld iy, _parseMultDiv
  73. call _parseApply
  74. ret nz
  75. jr .loop
  76. .plus:
  77. add hl, de
  78. ex de, hl
  79. ret
  80. .minus:
  81. or a ; clear carry
  82. sbc hl, de
  83. ex de, hl
  84. ret
  85. ; Parse (HL) as far as it can, that is, resolving expressions at its level or
  86. ; lower (anything but + and -).
  87. ; A is set to the last op it encountered. Unless there's an error, this can only
  88. ; be +, - or null. Null if we're done parsing, + and - if there's still work to
  89. ; do.
  90. ; (HL) points to last op encountered.
  91. ; DE is set to the numerical value of everything that was parsed left of (HL).
  92. _parseMultDiv:
  93. call _parseBitShift
  94. ret nz
  95. .loop:
  96. ; do we have an operator?
  97. or a
  98. ret z ; null char, we're done
  99. ; We have an operator. Resolve the rest of the expr then apply it.
  100. ld ix, .mult
  101. cp '*'
  102. jr z, .found
  103. ld ix, .div
  104. cp '/'
  105. jr z, .found
  106. ld ix, .mod
  107. cp '%'
  108. jr z, .found
  109. ; might not be an error, return success
  110. cp a
  111. ret
  112. .found:
  113. ld iy, _parseBitShift
  114. call _parseApply
  115. ret nz
  116. jr .loop
  117. .mult:
  118. push bc ; --> lvl 1
  119. ld b, h
  120. ld c, l
  121. call multDEBC ; --> HL
  122. pop bc ; <-- lvl 1
  123. ex de, hl
  124. ret
  125. .div:
  126. ; divide takes HL/DE
  127. ld a, l
  128. push bc ; --> lvl 1
  129. call divide
  130. ld e, c
  131. ld d, b
  132. pop bc ; <-- lvl 1
  133. ret
  134. .mod:
  135. call .div
  136. ex de, hl
  137. ret
  138. ; Same as _parseMultDiv, but a layer lower.
  139. _parseBitShift:
  140. call _parseNumber
  141. ret nz
  142. .loop:
  143. ; do we have an operator?
  144. or a
  145. ret z ; null char, we're done
  146. ; We have an operator. Resolve the rest of the expr then apply it.
  147. ld ix, .and
  148. cp '&'
  149. jr z, .found
  150. ld ix, .or
  151. cp 0x7c ; '|'
  152. jr z, .found
  153. ld ix, .xor
  154. cp '^'
  155. jr z, .found
  156. ld ix, .rshift
  157. cp '}'
  158. jr z, .found
  159. ld ix, .lshift
  160. cp '{'
  161. jr z, .found
  162. ; might not be an error, return success
  163. cp a
  164. ret
  165. .found:
  166. ld iy, _parseNumber
  167. call _parseApply
  168. ret nz
  169. jr .loop
  170. .and:
  171. ld a, h
  172. and d
  173. ld d, a
  174. ld a, l
  175. and e
  176. ld e, a
  177. ret
  178. .or:
  179. ld a, h
  180. or d
  181. ld d, a
  182. ld a, l
  183. or e
  184. ld e, a
  185. ret
  186. .xor:
  187. ld a, h
  188. xor d
  189. ld d, a
  190. ld a, l
  191. xor e
  192. ld e, a
  193. ret
  194. .rshift:
  195. ld a, e
  196. and 0xf
  197. ret z
  198. push bc ; --> lvl 1
  199. ld b, a
  200. .rshiftLoop:
  201. srl h
  202. rr l
  203. djnz .rshiftLoop
  204. ex de, hl
  205. pop bc ; <-- lvl 1
  206. ret
  207. .lshift:
  208. ld a, e
  209. and 0xf
  210. ret z
  211. push bc ; --> lvl 1
  212. ld b, a
  213. .lshiftLoop:
  214. sla l
  215. rl h
  216. djnz .lshiftLoop
  217. ex de, hl
  218. pop bc ; <-- lvl 1
  219. ret
  220. ; Parse first number of expression at (HL). A valid number is anything that can
  221. ; be parsed by EXPR_PARSE and is followed either by a null char or by any of the
  222. ; operator chars. This routines takes care of replacing an operator char with
  223. ; the null char before calling EXPR_PARSE and then replace the operator back
  224. ; afterwards.
  225. ; HL is moved to the char following the number having been parsed.
  226. ; DE contains the numerical result.
  227. ; A contains the operator char following the number (or null). Only on success.
  228. ; Z for success.
  229. _parseNumber:
  230. ; Special case 1: number starts with '-'
  231. ld a, (hl)
  232. cp '-'
  233. jr nz, .skip1
  234. ; We have a negative number. Parse normally, then subst from zero
  235. inc hl
  236. call _parseNumber
  237. push hl ; --> lvl 1
  238. ex af, af' ; preserve flags
  239. or a ; clear carry
  240. ld hl, 0
  241. sbc hl, de
  242. ex de, hl
  243. ex af, af' ; restore flags
  244. pop hl ; <-- lvl 1
  245. ret
  246. .skip1:
  247. ; End of special case 1
  248. ; Copy beginning of string to DE, we'll need it later
  249. ld d, h
  250. ld e, l
  251. ; Special case 2: we have a char literal. If we have a char literal, we
  252. ; don't want to go through the "_isOp" loop below because if that char
  253. ; is one of our operators, we're messing up our processing. So, set
  254. ; ourselves 3 chars further and continue from there. EXPR_PARSE will
  255. ; take care of validating those 3 chars.
  256. cp 0x27 ; apostrophe (') char
  257. jr nz, .skip2
  258. ; "'". advance HL by 3
  259. inc hl \ inc hl \ inc hl
  260. ; End of special case 2
  261. .skip2:
  262. dec hl ; offset "inc-hl-before" in loop
  263. .loop:
  264. inc hl
  265. ld a, (hl)
  266. call _isOp
  267. jr nz, .loop
  268. ; (HL) and A is an op or a null
  269. push af ; --> lvl 1 save op
  270. push hl ; --> lvl 2 save end of string
  271. ; temporarily put a null char instead of the op
  272. xor a
  273. ld (hl), a
  274. ex de, hl ; rewind to beginning of number
  275. call EXPR_PARSE ; --> DE
  276. ex af, af' ; keep result flags away while we restore (HL)
  277. pop hl ; <-- lvl 2, end of string
  278. pop af ; <-- lvl 1, saved op
  279. ld (hl), a
  280. ex af, af' ; restore Z from EXPR_PARSE
  281. ret nz
  282. ; HL is currently at the end of the number's string
  283. ; On success, have A be the operator char following the number
  284. ex af, af'
  285. ret
  286. ; Sets Z if A contains a valid operator char or a null char.
  287. _isOp:
  288. or a
  289. ret z
  290. push hl ; --> lvl 1
  291. ; Set A' to zero for quick end-of-table checks
  292. ex af, af'
  293. xor a
  294. ex af, af'
  295. ld hl, .exprChars
  296. .loop:
  297. cp (hl)
  298. jr z, .found
  299. ex af, af'
  300. cp (hl)
  301. jr z, .notFound ; end of table
  302. ex af, af'
  303. inc hl ; next char
  304. jr .loop
  305. .notFound:
  306. ex af, af' ; restore orig A
  307. inc a ; unset Z
  308. .found:
  309. ; Z already set
  310. pop hl ; <-- lvl 1
  311. ret
  312. .exprChars:
  313. .db "+-*/%&|^{}", 0