STEP.js 8.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245
  1. /**
  2. * STEP.js
  3. *
  4. * @author realor
  5. */
  6. const HEADER_SCHEMA = {}
  7. class STEPFile {
  8. name = null
  9. description = null
  10. schema = null // schema definition
  11. tags = {}
  12. references = []
  13. }
  14. class STEPParser {
  15. schema = null // classes map
  16. getSchemaTypes = null // called to get schema types
  17. onEntityCreated = null // called each time an entity is created
  18. decodeSTEPString(str) {
  19. return str.replace(/\\X2\\[\dA-F]{4}\\X0\\|\\X\\[\dA-F]{2}/gi, function(match) {
  20. var code = match.length === 12 ? match.substring(4, 8) : match.substring(3, 5)
  21. return String.fromCharCode(parseInt(code, 16))
  22. })
  23. }
  24. parse(text) {
  25. const t0 = Date.now()
  26. const file = new STEPFile()
  27. let tags = file.tags
  28. let references = file.references
  29. let lineCount = 0
  30. let builder = null
  31. let stack = []
  32. let token = ''
  33. let tag = null
  34. let inString = false
  35. let inComment = false
  36. for (let i = 0; i < text.length; i++) {
  37. let ch = text[i]
  38. if (inString) {
  39. if (ch === "'") {
  40. let nextCh = i < text.length - 1 ? text[i + 1] : null
  41. if (nextCh === "'") {
  42. // quote '' => '
  43. token += "'"
  44. i++
  45. } else {
  46. if (builder) {
  47. builder.add(this.decodeSTEPString(token))
  48. }
  49. token = ''
  50. inString = false
  51. }
  52. } else {
  53. token += ch
  54. }
  55. } else if (inComment) {
  56. token += ch
  57. if (token.endsWith('*/')) {
  58. inComment = false
  59. token = ''
  60. }
  61. } // out of string and comment
  62. else {
  63. if (ch === "'") {
  64. inString = true
  65. } else if (ch === ' ') {
  66. // ignore
  67. } else if (ch === '(') {
  68. let typeName = token.length > 0 ? token.toUpperCase() : 'Array'
  69. let schema = tag ? this.schema : HEADER_SCHEMA
  70. let newBuilder = new STEPBuilder(typeName, schema)
  71. if (builder) {
  72. // previous builder
  73. stack.push(builder) // save previous builder
  74. builder.add(newBuilder.instance)
  75. }
  76. builder = newBuilder
  77. token = ''
  78. } else if (ch === ',' || ch === ')') {
  79. if (builder === null) {
  80. console.warn('Parse error in line ' + (lineCount + 1))
  81. return null
  82. }
  83. if (token.length > 0) {
  84. if (token === '$') {
  85. builder.add(null)
  86. } else if ('0123456789-'.indexOf(token[0]) !== -1) {
  87. builder.add(parseFloat(token))
  88. } else if (token.charAt(0) === '#') {
  89. if (builder.instance) {
  90. references.push(new STEPReference(builder, token))
  91. }
  92. builder.add(null)
  93. } else {
  94. builder.add(token)
  95. }
  96. token = ''
  97. }
  98. if (ch === ')') {
  99. if (stack.length > 0) {
  100. builder = stack.pop()
  101. }
  102. }
  103. } else if (ch === ';') {
  104. if (tag) {
  105. if (builder && builder.instance) {
  106. tags[tag] = builder.instance
  107. if (this.onEntityCreated) {
  108. this.onEntityCreated(builder.instance)
  109. }
  110. }
  111. } // process header elements
  112. else {
  113. if (builder) {
  114. let instance = builder.instance
  115. if (instance instanceof FILE_SCHEMA) {
  116. file.schema = instance
  117. let schemaName = file.schema.Schemas[0]
  118. if (this.getSchemaTypes) {
  119. this.schema = this.getSchemaTypes(schemaName)
  120. }
  121. } else if (instance instanceof FILE_NAME) {
  122. file.name = instance
  123. } else if (instance instanceof FILE_DESCRIPTION) {
  124. file.description = instance
  125. }
  126. }
  127. }
  128. token = ''
  129. tag = null
  130. builder = null
  131. } else if (ch === '\t') {
  132. // ignore
  133. } else if (ch === '\n') {
  134. if (i === 0 || (i > 0 && text[i - 1] !== '\r')) lineCount++
  135. } else if (ch === '\r') {
  136. if (i === 0 || (i > 0 && text[i - 1] !== '\n')) lineCount++
  137. } else if (ch === '=') {
  138. token = token.trim()
  139. if (token.indexOf('#') === 0) {
  140. tag = token.trim()
  141. }
  142. token = ''
  143. } else {
  144. token += ch
  145. if (token.startsWith('/*')) {
  146. inComment = true
  147. }
  148. }
  149. }
  150. }
  151. // dereference references
  152. for (let i = 0; i < references.length; i++) {
  153. let reference = references[i]
  154. let instance = reference.instance
  155. let referencedInstance = tags[reference.tag] || null
  156. if (reference.property) {
  157. instance[reference.property] = referencedInstance
  158. } // Array
  159. else {
  160. let index = reference.index
  161. instance[index] = referencedInstance
  162. }
  163. }
  164. const t1 = Date.now()
  165. console.info('STEP File parsed in ' + (t1 - t0) + ' millis.')
  166. console.info('File contains ' + lineCount + ' lines.')
  167. return file
  168. }
  169. }
  170. class STEPBuilder {
  171. constructor(typeName, schema) {
  172. this.index = 0
  173. if (typeName === 'Array') {
  174. this.instance = []
  175. } else {
  176. let cls = schema ? schema[typeName] : HEADER_SCHEMA[typeName]
  177. if (cls) {
  178. this.instance = new cls()
  179. this.properties = Object.getOwnPropertyNames(this.instance)
  180. } else {
  181. this.instance = null
  182. console.warn('Unsupported entity ' + typeName)
  183. }
  184. }
  185. }
  186. add(element) {
  187. var instance = this.instance
  188. if (instance instanceof Array) {
  189. instance.push(element)
  190. } else if (instance !== null) {
  191. instance[this.properties[this.index]] = element
  192. }
  193. this.index++
  194. }
  195. }
  196. class STEPReference {
  197. constructor(builder, tag) {
  198. this.instance = builder.instance
  199. this.index = builder.index
  200. this.tag = tag // #<number>
  201. this.property = builder.properties ? builder.properties[builder.index] : null
  202. }
  203. }
  204. /* STEP header elements */
  205. class FILE_DESCRIPTION {
  206. Description = null
  207. ImplementationLevel = null
  208. }
  209. class FILE_NAME {
  210. Name = null
  211. TimeStamp = null
  212. Author = null
  213. Organization = null
  214. PreprocessorVersion = null
  215. Authorization = null
  216. Other = null
  217. }
  218. class FILE_SCHEMA {
  219. Schemas = null
  220. }
  221. HEADER_SCHEMA.FILE_DESCRIPTION = FILE_DESCRIPTION
  222. HEADER_SCHEMA.FILE_NAME = FILE_NAME
  223. HEADER_SCHEMA.FILE_SCHEMA = FILE_SCHEMA
  224. export { STEPParser, STEPFile }