2
0

parser.pas 13 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. {}
  2. //{$DEFINE mode_inter} //mode_inter->Modo intérprete mode_comp->Modo compilador
  3. unit Parser;
  4. {$mode objfpc}{$H+}
  5. interface
  6. uses
  7. Classes, SysUtils, LCLType, Dialogs, lclProc, Graphics, Forms,
  8. SynEditHighlighter, SynFacilBasic, XpresParser, XpresBas, XpresTypes, XpresElements,
  9. MisUtils, GenCod, FrameTabSession;
  10. type
  11. { TCompiler }
  12. TCompiler = class(TgenCod)
  13. private
  14. procedure CompileBlockIF;
  15. procedure CompileCurBlockNoEjec;
  16. function ProcesaAsignacion(var newVar: string): boolean;
  17. protected
  18. //function GetOperand: TOperand; override;
  19. procedure CaptureParams; override;
  20. procedure SkipWhites; override;
  21. public
  22. mem : TStringList; //Para almacenar el código de salida del compilador
  23. function EOBlock: boolean;
  24. function EOExpres: boolean;
  25. procedure CompileCurBlock;
  26. procedure CompilarArc;
  27. procedure Compilar(NombArc: string; LinArc: Tstrings);
  28. //Estos métodos solo sirven para hacer públicos los métodos protegidos
  29. procedure CreateVariable(const varName: string; typ: ttype);
  30. procedure CreateVariable(varName, varType: string);
  31. public //Inicialización
  32. constructor Create; override;
  33. destructor Destroy; override;
  34. end;
  35. var
  36. cxp : TCompiler;
  37. implementation
  38. uses FormPrincipal;
  39. //Funciones de acceso al compilador. Facilitan el acceso de forma resumida.
  40. procedure Code(cod: string);
  41. begin
  42. cxp.mem.Add(cod);
  43. end;
  44. procedure GenError(msg: string);
  45. begin
  46. cxp.GenError(msg);
  47. end;
  48. function HayError: boolean;
  49. begin
  50. Result := cxp.HayError;
  51. end;
  52. procedure CreateVariable(varName, varType: string);
  53. begin
  54. cxp.CreateVariable(varName, varType);
  55. end;
  56. //Métodos OVERRIDE
  57. function TCompiler.EOBlock: boolean;
  58. //Indica si se ha llegado el final de un bloque
  59. begin
  60. Result := cIn.tokType = tnBlkDelim;
  61. end;
  62. function TCompiler.EOExpres: boolean;
  63. //Indica si se ha llegado al final de una expresión
  64. begin
  65. Result := (cIn.tokType = tnExpDelim) or (cIn.tokType = tnEol);
  66. end;
  67. function TCompiler.ProcesaAsignacion(var newVar: string): boolean;
  68. {Verifica si la instrucción actual es de tipo asignación. Si es así, ejecuta la
  69. asignación. Si la variable a asignar no existe, se crea.
  70. Las asignaciones, se porcesan de forma diferente a las expresiones normales,
  71. porque, en este lenguaje, las asignaciones, también declaran variables y porque
  72. además se está permitiendo usar las asignaciones con el operador "=", en lugar
  73. del operador formal que es ":=".}
  74. var
  75. posIni, posFin: TPosCont;
  76. Op1: TOperand; //para representar a la variable
  77. opr: TxpOperator; //para representar al operador de asignación
  78. exp: TOperand; //para representar la expresión a asignar
  79. Nueva: Boolean;
  80. begin
  81. Result := false;
  82. if cIn.tokType <> tnIdentif then exit;
  83. //Sigue un identificador, verifica si ya ha sido declarado.
  84. if FindPredefName(cIn.tok) = eltNone then Nueva := true
  85. else Nueva := false;
  86. //Sigue un identificador desconocido. falta ver si es asignación.
  87. posIni := cIn.PosAct; //Guarda posición, por si acaso
  88. newVar := Cin.tok;
  89. cIn.Next; //toma identificador
  90. cIn.SkipWhitesNoEOL;
  91. if (cIn.tokType = tnOperator) and
  92. ( (cIn.tok = ':=') or (cIn.tok = '=')) then //Se acepta ambos operadores
  93. begin
  94. cIn.Next; //toma operador
  95. cIn.SkipWhitesNoEOL;
  96. //Evalua la expresión para deducir el tipo.
  97. // exp := GetOperand; //puede generar error
  98. GetExpressionE(0);
  99. exp := res; //guarda el resultado, para asignarlo luego
  100. posFin := cIn.PosAct; //guarda la posición final de la expresión.
  101. if Perr.HayError then exit(false); //sale con el puntero en la posición del error
  102. //Se pudo ejecutar la expresión. Ya se sabe el tipo
  103. if nueva then begin
  104. //debugln('Creando:'+newVar);
  105. cIn.PosAct := posIni; //Deja quí aquí, porque es un buen lugar en caso de error en CreateVariable().
  106. CreateVariable(newVar, exp.typ); //crea la variable
  107. if Perr.HayError then begin
  108. exit(false);
  109. end;
  110. end;
  111. cIn.PosAct := posIni; //retorna posición, para obtener fácilmente el operando
  112. Op1 := GetOperand; {Toma operando que puede ser la variable nueva creada, o algún
  113. identificador concoido, al que se le prentende asignar algo.}
  114. if Perr.HayError then exit;
  115. {Ya tenemos a los, dos operandos de la asignación. Lo más apropiado es usar
  116. la función Evaluar, para que las cosas sigan su curso, normal.}
  117. opr := Op1.FindOperator(':='); //Ubica a su operador de asignación. Debe existir
  118. cIn.PosAct := posFin; {Deja el cursor aquí, porque es el mejor lugar para el cursor
  119. en caso de error, y también porque aquí se debe quedar el
  120. cursor después de evaluar.}
  121. Oper(Op1, opr, exp); //Evalua en "res". Puede geenera error.
  122. if Perr.HayError then exit(false);
  123. exit(true); //si es asignación
  124. end;
  125. //no sigue asignación
  126. cIn.PosAct := posIni; //solo retorna posición
  127. end;
  128. procedure TCompiler.CompileCurBlockNoEjec;
  129. {Proecsa el bloque actual, sin ejecutar}
  130. var
  131. ejec0: boolean; //para guardar "ejec"
  132. begin
  133. ejec0 := ejec; //guarda estado actual (para permitir estructuras andiadas.)
  134. ejec := false; //deshabilita la ejecución
  135. CompileCurBlock; //procesa bloque else
  136. ejec := ejec0; //retorna estado.
  137. end;
  138. procedure TCompiler.CompileBlockIF;
  139. var
  140. valor, valor2: Boolean;
  141. begin
  142. cIn.Next; //toma IF
  143. GetBoolExpression; //evalua expresión
  144. if PErr.HayError then exit;
  145. valor := res.valBool;
  146. if cIn.tokL<> 'then' then begin
  147. GenError('Se esperaba "then".');
  148. exit;
  149. end;
  150. cIn.Next; //toma el THEN
  151. //Ejecuta el cuerpo del THEN
  152. if valor then CompileCurBlock else CompileCurBlockNoEjec;
  153. if PErr.HayError then exit;
  154. //Debe terminar con ENDIF, ELSE o ELSEIF
  155. if cIn.tokL = 'endif' then begin
  156. //Termina sentencia
  157. cIn.Next; //coge delimitador y termina normal
  158. end else if cIn.tokL = 'else' then begin
  159. //Hay un bloque ELSE
  160. cIn.Next; //coge "else"
  161. if valor then CompileCurBlockNoEjec else CompileCurBlock;
  162. if PErr.HayError then exit;
  163. //Debe seguir el delimitador de fin
  164. if cIn.tokL <> 'endif' then begin
  165. GenError('Se esperaba "ENDIF".');
  166. exit;
  167. end;
  168. cIn.Next; //coge delimitador y termina normal
  169. end else if cIn.tokL = 'elseif' then begin
  170. //Puede haber uno o varios 'elseif'
  171. cIn.Next; //coge "else"
  172. repeat
  173. GetBoolExpression; //evalua expresión
  174. if PErr.HayError then exit;
  175. valor2 := res.valBool;
  176. if cIn.tokL<> 'then' then begin
  177. GenError('Se esperaba "then".');
  178. exit;
  179. end;
  180. cIn.Next; //toma el THEN
  181. //Ejecuta el cuerpo del THEN
  182. if valor2 then CompileCurBlock else CompileCurBlockNoEjec;
  183. if PErr.HayError then exit;
  184. //Solo puede seguir ELSE, ELSEIF o ENDIF
  185. until cIn.tokL <> 'ELSEIF';
  186. //Solo puede seguir ELSE, o ENDIF
  187. if cIn.tokL = 'endif' then begin
  188. //Termina sentencia
  189. cIn.Next; //coge delimitador y termina normal
  190. end else if cIn.tokL = 'else' then begin
  191. //Hay un bloque ELSE en el ELSEIF
  192. cIn.Next; //coge "else"
  193. if valor or valor2 then CompileCurBlockNoEjec else CompileCurBlock;
  194. if PErr.HayError then exit;
  195. //Debe seguir el delimitador de fin
  196. if cIn.tokL <> 'endif' then begin
  197. GenError('Se esperaba "ENDIF".');
  198. exit;
  199. end;
  200. cIn.Next; //coge delimitador y termina normal
  201. end;
  202. end else begin //Debe ser error
  203. GenError('Se esperaba "ENDIF", "ELSE" o "ELSEIF".');
  204. exit;
  205. end;
  206. end;
  207. procedure TCompiler.CompileCurBlock;
  208. //Compila el bloque de código actual hasta encontrar un delimitador de bloque.
  209. var
  210. tmp: string;
  211. EsAsign: Boolean;
  212. begin
  213. cIn.SkipWhites; //ignora comentarios inciales
  214. //if config.fcMacros.marLin then ;
  215. while not cIn.Eof and not EOBlock do begin
  216. {Se espera una expresión o estructura. No hay problema en llamar a ProcesaAsignacion(),
  217. para procesar asignaciones con "=", ya que CompileCurBlock(), no se ejecuta al
  218. procesar las expresiones booleanas de un IF o un WHILE. }
  219. EsAsign := ProcesaAsignacion(tmp); //Verifica si es asignación
  220. if Perr.HayError then exit; //puede que se haya encontrado un error
  221. if EsAsign then begin //hay identificador nuevo
  222. //Se asume que es la asignación a una variable
  223. //No hay que hacer nada. Ya todo lo hizo "ProcesaAsignacion".
  224. end else if cIn.tokType = tnStruct then begin //es una estructura
  225. if cIn.tokL = 'if' then begin //condicional
  226. CompileBlockIF;
  227. if HayError then exit;
  228. end else begin
  229. GenError('Error de diseño. Estructura no implementada.');
  230. exit;
  231. end;
  232. end else begin //debe ser una expresión
  233. GetExpressionE(0);
  234. if perr.HayError then exit; //aborta
  235. end;
  236. if stop then exit;
  237. //Se espera delimitador
  238. if cIn.Eof then break; //sale por fin de archivo
  239. //Busca delimitador de bloque
  240. cIn.SkipWhitesNoEOL;
  241. if cIn.tokType=tnEol then begin //encontró delimitador de expresión
  242. cIn.Next; //lo toma
  243. cIn.SkipWhites; //quita espacios
  244. end else if EOBlock then begin //hay delimitador de bloque
  245. exit; //no lo toma
  246. end else begin //hay otra cosa, debe ser un error.
  247. GenError('Error de sintaxis.');
  248. exit;
  249. end;
  250. end;
  251. end;
  252. procedure TCompiler.CompilarArc;
  253. //Compila un programa en el contexto actual
  254. begin
  255. // CompilarAct;
  256. Perr.Clear;
  257. cIn.SkipWhites;
  258. if cIn.Eof then begin
  259. // GenError('Se esperaba "begin", "var", "type" o "const".');
  260. exit;
  261. end;
  262. //empiezan las declaraciones
  263. Cod_StartData;
  264. Cod_StartProgram;
  265. //codifica el contenido
  266. ejec := true; //pome para ejecutar
  267. CompileCurBlock; //compila el cuerpo
  268. if Perr.HayError then exit;
  269. if not cIn.Eof then begin
  270. //Algo ha quedado sin proesar
  271. if not stop then begin //Si no se detuvo voluntariamente
  272. GenError('Error de sintaxis: ' + cIn.tok);
  273. exit; //sale
  274. end;
  275. end;
  276. cIn.Next; //coge "end"
  277. end;
  278. procedure TCompiler.Compilar(NombArc: string; LinArc: Tstrings);
  279. //Ejecuta el contenido de un archivo
  280. var
  281. ses: TfraTabSession;
  282. begin
  283. //se pone en un "try" para capturar errores y para tener un punto salida de salida
  284. //único
  285. if ejecProg then begin
  286. GenError('Ya se está ejecutando un programa actualmente.');
  287. exit; //sale directamente
  288. end;
  289. try
  290. ejecProg := true; //marca bandera
  291. frmPrincipal.ejecMac := true; //indica que se está ejecutando la macro
  292. Perr.IniError;
  293. ClearVars; //limpia las variables
  294. ClearFuncs; //limpia las funciones
  295. mem.Clear; //limpia salida
  296. cIn.ClearAll; //elimina todos los Contextos de entrada
  297. ExprLevel := 0; //inicia
  298. //compila el archivo abierto
  299. // con := PosAct; //Guarda posición y referencia a contenido actual
  300. cIn.NewContextFromFile(NombArc,LinArc); //Crea nuevo contenido
  301. if PErr.HayError then exit;
  302. CompilarArc; //puede dar error
  303. Cod_EndProgram; //da oportunidad de hacer verificaciones
  304. cIn.RemoveContext; //es necesario por dejar limpio
  305. if PErr.HayError then exit; //sale
  306. // PosAct := con; //recupera el contenido actual
  307. // PPro.GenArchivo(ArcSal);
  308. // ShowResult; //muestra el resultado
  309. finally
  310. ejecProg := false;
  311. //tareas de finalización
  312. frmPrincipal.ejecMac := false;
  313. if frmPrincipal.GetCurSession(ses) then begin
  314. ses.UpdatePanInfoConn;
  315. end;
  316. end;
  317. end;
  318. procedure TCompiler.CreateVariable(const varName: string; typ: ttype);
  319. begin
  320. Inherited;
  321. end;
  322. procedure TCompiler.CreateVariable(varName, varType: string);
  323. begin
  324. Inherited;
  325. end;
  326. procedure TCompiler.CaptureParams;
  327. //Lee los parámetros de una función en la función interna funcs[0]
  328. begin
  329. cIn.SkipWhitesNoEOL;
  330. func0.ClearParams; //inicia parámetros
  331. if EOBlock or EOExpres then begin
  332. //no tiene parámetros
  333. end else begin
  334. //debe haber parámetros
  335. repeat
  336. GetExpressionE(0, true); //captura parámetro
  337. if perr.HayError then exit; //aborta
  338. //guarda tipo de parámetro, para después comparar todos los parámetros leídos
  339. func0.CreateParam('', res.typ);
  340. if cIn.tok = ',' then begin
  341. cIn.Next; //toma separador
  342. cIn.SkipWhitesNoEOL;
  343. end else begin
  344. //no sigue separador de parámetros,
  345. //debe terminar la lista de parámetros
  346. //¿Verificar EOBlock or EOExpres ?
  347. break;
  348. end;
  349. until false;
  350. end;
  351. end;
  352. procedure TCompiler.SkipWhites;
  353. {En este lenguaje, se consideran delimitadores a los saltos de línea, así que no se
  354. deben saltar.}
  355. begin
  356. cIn.SkipWhitesNoEOL;
  357. end;
  358. //procedure TCompilerBase.ShowError
  359. constructor TCompiler.Create;
  360. begin
  361. inherited Create;
  362. mem := TStringList.Create; //crea lista para almacenar ensamblador
  363. //se puede definir la sintaxis aquí o dejarlo para StartSyntax()
  364. StartSyntax; //Debe hacerse solo una vez al inicio
  365. if HayError then ShowError;
  366. end;
  367. destructor TCompiler.Destroy;
  368. begin
  369. mem.Free; //libera
  370. inherited Destroy;
  371. end;
  372. initialization
  373. //Es necesario crear solo una instancia del compilador.
  374. cxp := TCompiler.Create; //Crea una instancia del compilador
  375. finalization
  376. cxp.Destroy;
  377. end.