UNPKG

21.5 kBtext/coffeescriptView Raw
1# This scanner uses the recursive descent method.
2#
3# The char pointers token_str and scan_str are pointers to the input string as
4# in the following example.
5#
6# | g | a | m | m | a | | a | l | p | h | a |
7# ^ ^
8# token_str scan_str
9#
10# The char pointer token_buf points to a malloc buffer.
11#
12# | g | a | m | m | a | \0 |
13# ^
14# token_buf
15#
16# In the sequence of method invocations for scanning,
17# first we do the calls for scanning the operands
18# of the operators of least precedence.
19# So, since precedence in maths goes something like
20# (form high to low) exponents, mult/div, plus/minus
21# so we scan first for terms, then factors, then powers.
22# That's the general idea, but of course we also have to deal
23# with things like parens, non-commutative
24# dot (or inner) product, assignments and tests,
25# function calls etc.
26# Note that a^1/2 is, correctly, a/2, not, incorrectly, sqrt(a),
27# see comment in related test in power.coffee for more about this.
28
29# Notes:
30#
31# Formerly add() and multiply() were used to construct expressions but
32# this preevaluation caused problems.
33#
34# For example, suppose A has the floating point value inf.
35#
36# Before, the expression A/A resulted in 1 because the scanner would
37# divide the symbols.
38#
39# After removing add() and multiply(), A/A results in nan which is the
40# correct result.
41#
42# The functions negate() and inverse() are used but they do not cause
43# problems with preevaluation of symbols.
44
45
46T_INTEGER = 1001
47T_DOUBLE = 1002
48T_SYMBOL = 1003
49T_FUNCTION = 1004
50T_NEWLINE = 1006
51T_STRING = 1007
52T_GTEQ = 1008
53T_LTEQ = 1009
54T_EQ = 1010
55T_NEQ = 1011
56T_QUOTASSIGN = 1012
57
58token = ""
59newline_flag = 0
60meta_mode = 0
61
62input_str = 0
63scan_str = 0
64token_str = 0
65token_buf = 0
66
67lastFoundSymbol = null
68symbolsRightOfAssignment = null
69symbolsLeftOfAssignment = null
70isSymbolLeftOfAssignment = null
71scanningParameters = null
72functionInvokationsScanningStack = null
73skipRootVariableToBeSolved = false
74assignmentFound = null
75
76
77# Returns number of chars scanned and expr on stack.
78
79# Returns zero when nothing left to scan.
80
81# takes a string
82
83scanned = ""
84scan = (s) ->
85 if DEBUG then console.log "#### scanning " + s
86 #if s=="y=x"
87 # debugger
88 #if s=="y"
89 # debugger
90 #if s=="i=sqrt(-1)"
91 # debugger
92
93 lastFoundSymbol = null
94 symbolsRightOfAssignment = []
95 symbolsLeftOfAssignment = []
96 isSymbolLeftOfAssignment = true
97 scanningParameters = []
98 functionInvokationsScanningStack = [""]
99 assignmentFound = false
100
101
102 scanned = s
103 meta_mode = 0
104 expanding++
105 input_str = 0
106 scan_str = 0
107 get_next_token()
108 if (token == "")
109 push(symbol(NIL))
110 expanding--
111 return 0
112 scan_stmt()
113 expanding--
114
115 if !assignmentFound
116 symbolsInExpressionsWithoutAssignments = symbolsInExpressionsWithoutAssignments.concat symbolsLeftOfAssignment
117
118 return token_str - input_str
119
120# takes a string
121scan_meta = (s) ->
122 scanned = s
123 meta_mode = 1
124 expanding++
125 input_str = 0
126 scan_str = 0
127 get_next_token()
128 if (token == "")
129 push(symbol(NIL))
130 expanding--
131 return 0
132 scan_stmt()
133 expanding--
134 return token_str - input_str
135
136scan_stmt = ->
137 scan_relation()
138
139 assignmentIsOfQuotedType = false
140
141 if token == T_QUOTASSIGN
142 assignmentIsOfQuotedType = true
143
144 if (token == T_QUOTASSIGN or token == '=')
145 symbolLeftOfAssignment = lastFoundSymbol
146 if DEBUG then console.log("assignment!")
147 assignmentFound = true
148 isSymbolLeftOfAssignment = false
149
150 get_next_token()
151 push_symbol(SETQ)
152 swap()
153
154 # if it's a := then add a quote
155 if (assignmentIsOfQuotedType)
156 push_symbol(QUOTE)
157
158 scan_relation()
159
160 # if it's a := then you have to list
161 # together the quote and its argument
162 if assignmentIsOfQuotedType
163 list(2)
164
165 list(3)
166
167 isSymbolLeftOfAssignment = true
168
169 if codeGen
170
171 # in case of re-assignment, the symbol on the
172 # left will also be in the set of the symbols
173 # on the right. In that case just remove it from
174 # the symbols on the right.
175 indexOfSymbolLeftOfAssignment = symbolsRightOfAssignment.indexOf(symbolLeftOfAssignment)
176 if indexOfSymbolLeftOfAssignment != -1
177 symbolsRightOfAssignment.splice(indexOfSymbolLeftOfAssignment, 1)
178 symbolsHavingReassignments.push symbolLeftOfAssignment
179
180 # print out the immediate dependencies
181 if DEBUG
182 console.log "locally, " + symbolLeftOfAssignment + " depends on: "
183 for i in symbolsRightOfAssignment
184 console.log " " + i
185
186 # ok add the local dependencies to the existing
187 # dependencies of this left-value symbol
188
189 # create the exiting dependencies list if it doesn't exist
190 symbolsDependencies[symbolLeftOfAssignment] ?= []
191 existingDependencies = symbolsDependencies[symbolLeftOfAssignment]
192
193 # copy over the new dependencies to the existing
194 # dependencies avoiding repetitions
195 for i in symbolsRightOfAssignment
196 if existingDependencies.indexOf(i) == -1
197 existingDependencies.push i
198
199 symbolsRightOfAssignment = []
200
201scan_relation = ->
202 scan_expression()
203 switch (token)
204 when T_EQ
205 push_symbol(TESTEQ)
206 swap()
207 get_next_token()
208 scan_expression()
209 list(3)
210 when T_NEQ
211 push_symbol(NOT)
212 swap()
213 push_symbol(TESTEQ)
214 swap()
215 get_next_token()
216 scan_expression()
217 list(3)
218 list(2)
219 when T_LTEQ
220 push_symbol(TESTLE)
221 swap()
222 get_next_token()
223 scan_expression()
224 list(3)
225 when T_GTEQ
226 push_symbol(TESTGE)
227 swap()
228 get_next_token()
229 scan_expression()
230 list(3)
231 when '<'
232 push_symbol(TESTLT)
233 swap()
234 get_next_token()
235 scan_expression()
236 list(3)
237 when '>'
238 push_symbol(TESTGT)
239 swap()
240 get_next_token()
241 scan_expression()
242 list(3)
243
244scan_expression = ->
245 h = tos
246 switch token
247 when '+'
248 get_next_token()
249 scan_term()
250 when '-'
251 get_next_token()
252 scan_term()
253 negate()
254 else
255 scan_term()
256
257 while (newline_flag == 0 && (token == '+' || token == '-'))
258 if (token == '+')
259 get_next_token()
260 scan_term()
261 else
262 get_next_token()
263 scan_term()
264 negate()
265
266 if (tos - h > 1)
267 list(tos - h)
268 push_symbol(ADD)
269 swap()
270 cons()
271
272is_factor = ->
273
274 if token.charCodeAt?(0) == dotprod_unicode
275 return 1
276
277 switch (token)
278 when '*', '/'
279 return 1
280 when '(', T_SYMBOL, T_FUNCTION, T_INTEGER, T_DOUBLE, T_STRING
281 if (newline_flag) # implicit mul can't cross line
282 scan_str = token_str # better error display
283 return 0
284 else
285 return 1
286 return 0
287
288
289simplify_1_in_products = (tos,h) ->
290 if (tos > h && isrational(stack[tos - 1]) && equaln(stack[tos - 1], 1))
291 pop()
292
293# calculate away consecutive constants
294multiply_consecutive_constants = (tos,h)->
295 if (tos > h + 1 && isnum(stack[tos - 2]) && isnum(stack[tos - 1]))
296 multiply()
297
298
299scan_term = ->
300 h = tos
301
302 scan_factor()
303
304 if parse_time_simplifications
305 simplify_1_in_products(tos,h)
306
307 while (is_factor())
308 if (token == '*')
309 get_next_token()
310 scan_factor()
311 else if (token == '/')
312 # in case of 1/... then
313 # we scanned the 1, we get rid
314 # of it because otherwise it becomes
315 # an extra factor that wasn't there and
316 # things like
317 # 1/(2*a) become 1*(1/(2*a))
318 simplify_1_in_products(tos,h)
319 get_next_token()
320 scan_factor()
321 inverse()
322 else if (token.charCodeAt?(0) == dotprod_unicode)
323 get_next_token()
324 push_symbol(INNER)
325 swap()
326 scan_factor()
327 list(3)
328
329 else
330 scan_factor()
331
332 if parse_time_simplifications
333 multiply_consecutive_constants(tos,h)
334 simplify_1_in_products(tos,h)
335
336 if (h == tos)
337 push_integer(1)
338 else if (tos - h > 1)
339 list(tos - h)
340 push_symbol(MULTIPLY)
341 swap()
342 cons()
343
344scan_power = ->
345 if (token == '^')
346 get_next_token()
347 push_symbol(POWER)
348 swap()
349 scan_factor()
350 list(3)
351
352scan_index = (h) ->
353 #console.log "[ as index"
354 get_next_token()
355 push_symbol(INDEX)
356 swap()
357 scan_expression()
358 while (token == ',')
359 get_next_token()
360 scan_expression()
361 if (token != ']')
362 scan_error("] expected")
363 get_next_token()
364 list(tos - h)
365
366
367scan_factor = ->
368
369 h = tos
370
371 #console.log "scan_factor token: " + token
372
373 if (token == '(')
374 scan_subexpr()
375 else if (token == T_SYMBOL)
376 scan_symbol()
377 else if (token == T_FUNCTION)
378 scan_function_call_with_function_name()
379 else if token == '['
380 #console.log "[ as tensor"
381 #debugger
382 scan_tensor()
383 else if (token == T_INTEGER)
384 bignum_scan_integer(token_buf)
385 get_next_token()
386 else if (token == T_DOUBLE)
387 bignum_scan_float(token_buf)
388 get_next_token()
389 else if (token == T_STRING)
390 scan_string()
391 else
392 scan_error("syntax error")
393
394
395 # after the main initial part of the factor that
396 # we just scanned above,
397 # we can get an arbitrary about of appendages
398 # of the form ...[...](...)...
399 # These are all, respectively,
400 # - index references (as opposed to tensor definition) and
401 # - function calls without an explicit function name
402 # (instead of subexpressions or parameters of function
403 # definitions or function calls with an explicit function
404 # name), respectively
405 while token == '[' or token == '(' and newline_flag == 0
406 if token == '['
407 scan_index(h)
408 else if token == '('
409 #console.log "( as function call without function name "
410 scan_function_call_without_function_name()
411
412
413
414 while (token == '!')
415 get_next_token()
416 push_symbol(FACTORIAL)
417 swap()
418 list(2)
419
420 # in theory we could already count the
421 # number of transposes and simplify them
422 # away, but it's not that clean to have
423 # multiple places where that happens, and
424 # the parser is not the place.
425 while (token.charCodeAt?(0) == transpose_unicode)
426 get_next_token()
427 push_symbol(TRANSPOSE)
428 swap()
429 list(2)
430
431 scan_power()
432
433
434addSymbolRightOfAssignment = (theSymbol) ->
435 if predefinedSymbolsInGlobalScope_doNotTrackInDependencies.indexOf(theSymbol) == -1 and
436 symbolsRightOfAssignment.indexOf(theSymbol) == -1 and
437 symbolsRightOfAssignment.indexOf("'"+theSymbol) == -1 and
438 !skipRootVariableToBeSolved
439 if DEBUG then console.log("... adding symbol: " + theSymbol + " to the set of the symbols right of assignment")
440 prefixVar = ""
441 for i in [1...functionInvokationsScanningStack.length]
442 if functionInvokationsScanningStack[i] != ""
443 prefixVar += functionInvokationsScanningStack[i] + "_" + i + "_"
444
445 theSymbol = prefixVar + theSymbol
446 symbolsRightOfAssignment.push theSymbol
447
448addSymbolLeftOfAssignment = (theSymbol) ->
449 if predefinedSymbolsInGlobalScope_doNotTrackInDependencies.indexOf(theSymbol) == -1 and
450 symbolsLeftOfAssignment.indexOf(theSymbol) == -1 and
451 symbolsLeftOfAssignment.indexOf("'"+theSymbol) == -1 and
452 !skipRootVariableToBeSolved
453 if DEBUG then console.log("... adding symbol: " + theSymbol + " to the set of the symbols left of assignment")
454 prefixVar = ""
455 for i in [1...functionInvokationsScanningStack.length]
456 if functionInvokationsScanningStack[i] != ""
457 prefixVar += functionInvokationsScanningStack[i] + "_" + i + "_"
458
459 theSymbol = prefixVar + theSymbol
460 symbolsLeftOfAssignment.push theSymbol
461
462scan_symbol = ->
463 if (token != T_SYMBOL)
464 scan_error("symbol expected")
465 if (meta_mode && token_buf.length == 1)
466 switch (token_buf[0])
467 when 'a'
468 push(symbol(METAA))
469 when 'b'
470 push(symbol(METAB))
471 when 'x'
472 push(symbol(METAX))
473 else
474 push(usr_symbol(token_buf))
475 else
476 push(usr_symbol(token_buf))
477 #console.log "found symbol: " + token_buf
478
479 if scanningParameters.length == 0
480 if DEBUG then console.log "out of scanning parameters, processing " + token_buf
481 lastFoundSymbol = token_buf
482 if isSymbolLeftOfAssignment
483 addSymbolLeftOfAssignment token_buf
484 else
485 if DEBUG then console.log "still scanning parameters, skipping " + token_buf
486 if isSymbolLeftOfAssignment
487 addSymbolRightOfAssignment "'" + token_buf
488
489 if DEBUG then console.log("found symbol: " + token_buf + " left of assignment: " + isSymbolLeftOfAssignment)
490
491 # if we were looking at the right part of an assignment while we
492 # found the symbol, then add it to the "symbolsRightOfAssignment"
493 # set (we check for duplications)
494 if !isSymbolLeftOfAssignment
495 addSymbolRightOfAssignment token_buf
496 get_next_token()
497
498scan_string = ->
499 new_string(token_buf)
500 get_next_token()
501
502scan_function_call_with_function_name = ->
503 if DEBUG then console.log "-- scan_function_call_with_function_name start"
504 n = 1 # the parameter number as we scan parameters
505 p = new U()
506 p = usr_symbol(token_buf)
507
508 push(p)
509 get_next_token() # function name
510 functionName = token_buf
511 if functionName == "roots" or functionName == "defint" or functionName == "sum" or functionName == "product" or functionName == "for"
512 functionInvokationsScanningStack.push token_buf
513 lastFoundSymbol = token_buf
514 if !isSymbolLeftOfAssignment
515 addSymbolRightOfAssignment token_buf
516
517 get_next_token() # 1st parameter
518 scanningParameters.push true
519 if (token != ')')
520 scan_stmt()
521 n++
522 while (token == ',')
523 get_next_token()
524 # roots' disappearing variable, if there, is the second one
525 if n == 2 and functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("roots") != -1
526 symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x)
527 skipRootVariableToBeSolved = true
528 # sums' disappearing variable, is alsways the second one
529 if n == 2 and functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("sum") != -1
530 symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("sum_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x)
531 skipRootVariableToBeSolved = true
532 # product's disappearing variable, is alsways the second one
533 if n == 2 and functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("product") != -1
534 symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("product_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x)
535 skipRootVariableToBeSolved = true
536 # for's disappearing variable, is alsways the second one
537 if n == 2 and functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("for") != -1
538 symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("for_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x)
539 skipRootVariableToBeSolved = true
540 # defint's disappearing variables can be in positions 2,5,8...
541 if functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("defint") != -1 and
542 (n == 2 or (n>2 and ((n-2) % 3 == 0)))
543 symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("defint_" + (functionInvokationsScanningStack.length - 1) + "_" + token_buf)).test(x)
544 skipRootVariableToBeSolved = true
545
546 scan_stmt()
547 skipRootVariableToBeSolved = false
548 n++
549
550 # todo refactor this, there are two copies
551 # this catches the case where the "roots" variable is not specified
552 if n == 2 and functionInvokationsScanningStack[functionInvokationsScanningStack.length - 1].indexOf("roots") != -1
553 symbolsRightOfAssignment = symbolsRightOfAssignment.filter (x) -> !(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_" + "x")).test(x)
554
555 scanningParameters.pop()
556
557 for i in [0..symbolsRightOfAssignment.length]
558 if symbolsRightOfAssignment[i]?
559 if functionName == "roots"
560 symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("roots_" + (functionInvokationsScanningStack.length - 1) + "_"),"")
561 if functionName == "defint"
562 symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("defint_" + (functionInvokationsScanningStack.length - 1) + "_"),"")
563 if functionName == "sum"
564 symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("sum_" + (functionInvokationsScanningStack.length - 1) + "_"),"")
565 if functionName == "product"
566 symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("product_" + (functionInvokationsScanningStack.length - 1) + "_"),"")
567 if functionName == "for"
568 symbolsRightOfAssignment[i] = symbolsRightOfAssignment[i].replace(new RegExp("for_" + (functionInvokationsScanningStack.length - 1) + "_"),"")
569
570 if (token != ')')
571 scan_error(") expected")
572
573 get_next_token()
574 list(n)
575 if functionName == "roots" or functionName == "defint" or functionName == "sum" or functionName == "product" or functionName == "for"
576 functionInvokationsScanningStack.pop()
577 if functionName == symbol(PATTERN).printname
578 patternHasBeenFound = true
579
580 if DEBUG then console.log "-- scan_function_call_with_function_name end"
581
582scan_function_call_without_function_name = ->
583 if DEBUG then console.log "-- scan_function_call_without_function_name start"
584
585 # the function will have to be looked up
586 # at runtime
587 push_symbol(EVAL)
588 swap()
589 list(2)
590
591 n = 1 # the parameter number as we scan parameters
592 get_next_token() # left paren
593 scanningParameters.push true
594 if (token != ')')
595 scan_stmt()
596 n++
597 while (token == ',')
598 get_next_token()
599 scan_stmt()
600 n++
601
602 scanningParameters.pop()
603
604
605 if (token != ')')
606 scan_error(") expected")
607
608 get_next_token()
609 list(n)
610
611 if DEBUG then console.log "-- scan_function_call_without_function_name end: " + stack[tos-1]
612
613# scan subexpression
614
615scan_subexpr = ->
616 n = 0
617 if (token != '(')
618 scan_error("( expected")
619 get_next_token()
620 scan_stmt()
621 if (token != ')')
622 scan_error(") expected")
623 get_next_token()
624
625scan_tensor = ->
626 n = 0
627 if (token != '[')
628 scan_error("[ expected")
629
630 get_next_token()
631
632 #console.log "scanning the next statement"
633 scan_stmt()
634
635 n = 1
636 while (token == ',')
637 get_next_token()
638 scan_stmt()
639 n++
640
641 #console.log "building tensor with elements number: " + n
642 build_tensor(n)
643
644 if (token != ']')
645 scan_error("] expected")
646 get_next_token()
647
648scan_error = (errmsg) ->
649 errorMessage = ""
650
651 # try not to put question mark on orphan line
652
653 while (input_str != scan_str)
654 if ((scanned[input_str] == '\n' || scanned[input_str] == '\r') && input_str + 1 == scan_str)
655 break
656 errorMessage += scanned[input_str++]
657
658 errorMessage += " ? "
659
660 while (scanned[input_str] && (scanned[input_str] != '\n' && scanned[input_str] != '\r'))
661 errorMessage += scanned[input_str++]
662
663 errorMessage += '\n'
664
665 stop(errmsg)
666
667# There are n expressions on the stack, possibly tensors.
668#
669# This function assembles the stack expressions into a single tensor.
670#
671# For example, at the top level of the expression ((a,b),(c,d)), the vectors
672# (a,b) and (c,d) would be on the stack.
673
674# takes an integer
675build_tensor = (n) ->
676 # int i, j, k, ndim, nelem
677
678 i = 0
679
680 save()
681
682 p2 = alloc_tensor(n)
683 p2.tensor.ndim = 1
684 p2.tensor.dim[0] = n
685 for i in [0...n]
686 p2.tensor.elem[i] = stack[tos-n+i]
687
688 check_tensor_dimensions p2
689
690 moveTos tos - n
691
692 push(p2)
693
694 restore()
695
696get_next_token = ->
697 newline_flag = 0
698 while (1)
699 get_token()
700 if (token != T_NEWLINE)
701 break
702 newline_flag = 1
703 if DEBUG then console.log "get_next_token token: " + token
704 #if token == ')'
705 # debugger
706
707get_token = ->
708 # skip spaces
709 while (isspace(scanned[scan_str]))
710 if (scanned[scan_str] == '\n' || scanned[scan_str] == '\r')
711 token = T_NEWLINE
712 scan_str++
713 return
714 scan_str++
715
716 token_str = scan_str
717
718 # end of string?
719
720 if (scan_str == scanned.length)
721 token = ""
722 return
723
724 # number?
725
726 if (isdigit(scanned[scan_str]) || scanned[scan_str] == '.')
727 while (isdigit(scanned[scan_str]))
728 scan_str++
729 if (scanned[scan_str] == '.')
730 scan_str++
731 while (isdigit(scanned[scan_str]))
732 scan_str++
733 if (scanned[scan_str] == 'e' && (scanned[scan_str+1] == '+' || scanned[scan_str+1] == '-' || isdigit(scanned[scan_str+1])))
734 scan_str += 2
735 while (isdigit(scanned[scan_str]))
736 scan_str++
737 token = T_DOUBLE
738 else
739 token = T_INTEGER
740 update_token_buf(token_str, scan_str)
741 return
742
743 # symbol?
744
745 if (isalpha(scanned[scan_str]))
746 while (isalnumorunderscore(scanned[scan_str]))
747 scan_str++
748 if (scanned[scan_str] == '(')
749 token = T_FUNCTION
750 else
751 token = T_SYMBOL
752 update_token_buf(token_str, scan_str)
753 return
754
755 # string ?
756
757 if (scanned[scan_str] == '"')
758 scan_str++
759 while (scanned[scan_str] != '"')
760 #if (scan_str == scanned.length || scanned[scan_str] == '\n' || scanned[scan_str] == '\r')
761 if (scan_str == scanned.length - 1)
762 scan_str++
763 scan_error("runaway string")
764 scan_str--
765 scan_str++
766 scan_str++
767 token = T_STRING
768 update_token_buf(token_str + 1, scan_str - 1)
769 return
770
771 # comment?
772
773 if (scanned[scan_str] == '#' || scanned[scan_str] == '-' && scanned[scan_str+1] == '-')
774 while (scanned[scan_str] && scanned[scan_str] != '\n' && scanned[scan_str] != '\r')
775 scan_str++
776 if (scanned[scan_str])
777 scan_str++
778 token = T_NEWLINE
779 return
780
781 # quote-assignment
782 if (scanned[scan_str] == ':' && scanned[scan_str+1] == '=')
783 scan_str += 2
784 token = T_QUOTASSIGN
785 return
786
787 # relational operator?
788 if (scanned[scan_str] == '=' && scanned[scan_str+1] == '=')
789 scan_str += 2
790 token = T_EQ
791 return
792
793 # != operator. It's a little odd because
794 # "!" is not a "not", which would make things consistent.
795 # (it's used for factorial).
796 # An alternative would be to use "<>" but it's not used
797 # a lot in other languages...
798 if (scanned[scan_str] == '!' && scanned[scan_str+1] == '=')
799 scan_str += 2
800 token = T_NEQ
801 return
802
803 if (scanned[scan_str] == '<' && scanned[scan_str+1] == '=')
804 scan_str += 2
805 token = T_LTEQ
806 return
807
808 if (scanned[scan_str] == '>' && scanned[scan_str+1] == '=')
809 scan_str += 2
810 token = T_GTEQ
811 return
812
813 # single char token
814
815 token = scanned[scan_str++]
816
817# both strings
818update_token_buf = (a,b) ->
819
820 token_buf = scanned.substring(a,b)
821
822
823$.scan = scan